<?xml version="1.0" encoding="utf-8" standalone="no"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
        "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">

<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <title></title>
    <link href="../Styles/sure3.css" rel="stylesheet" type="text/css"/>
    <link href="../Styles/base1.css" rel="stylesheet" type="text/css"/>
</head>

<body>
<div class="markdown-body entry-content" itemprop="text"><h1><a href="#对象的扩展"  class="anchor" id="user-content-对象的扩展"><svg  class="octicon octicon-link" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>对象的扩展</h1>
    <h2><a href="#属性的简洁表示法"  class="anchor" id="user-content-属性的简洁表示法"><svg  class="octicon octicon-link" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>属性的简洁表示法</h2>
    <p>ES6 允许直接写入变量和函数，作为对象的属性和方法。这样的书写更加简洁。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">const</span> <span class="pl-c1">foo</span> <span class="pl-k">=</span> <span class="pl-s"><span class="pl-pds">'</span>bar<span class="pl-pds">'</span></span>;
<span class="pl-k">const</span> <span class="pl-c1">baz</span> <span class="pl-k">=</span> {foo};
baz <span class="pl-c"><span class="pl-c">//</span> {foo: "bar"}</span>

<span class="pl-c"><span class="pl-c">//</span> 等同于</span>
<span class="pl-k">const</span> <span class="pl-c1">baz</span> <span class="pl-k">=</span> {foo<span class="pl-k">:</span> foo};</pre></div>
    <p>上面代码表明，ES6 允许在对象之中，直接写变量。这时，属性名为变量名, 属性值为变量的值。下面是另一个例子。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">function</span> <span class="pl-en">f</span>(<span class="pl-smi">x</span>, <span class="pl-smi">y</span>) {
  <span class="pl-k">return</span> {x, y};
}

<span class="pl-c"><span class="pl-c">//</span> 等同于</span>

<span class="pl-k">function</span> <span class="pl-en">f</span>(<span class="pl-smi">x</span>, <span class="pl-smi">y</span>) {
  <span class="pl-k">return</span> {x<span class="pl-k">:</span> x, y<span class="pl-k">:</span> y};
}

<span class="pl-en">f</span>(<span class="pl-c1">1</span>, <span class="pl-c1">2</span>) <span class="pl-c"><span class="pl-c">//</span> Object {x: 1, y: 2}</span></pre></div>
    <p>除了属性简写，方法也可以简写。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">const</span> <span class="pl-c1">o</span> <span class="pl-k">=</span> {
  <span class="pl-en">method</span>() {
    <span class="pl-k">return</span> <span class="pl-s"><span class="pl-pds">"</span>Hello!<span class="pl-pds">"</span></span>;
  }
};

<span class="pl-c"><span class="pl-c">//</span> 等同于</span>

<span class="pl-k">const</span> <span class="pl-c1">o</span> <span class="pl-k">=</span> {
  <span class="pl-en">method</span><span class="pl-k">:</span> <span class="pl-k">function</span>() {
    <span class="pl-k">return</span> <span class="pl-s"><span class="pl-pds">"</span>Hello!<span class="pl-pds">"</span></span>;
  }
};</pre></div>
    <p>下面是一个实际的例子。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">let</span> birth <span class="pl-k">=</span> <span class="pl-s"><span class="pl-pds">'</span>2000/01/01<span class="pl-pds">'</span></span>;

<span class="pl-k">const</span> <span class="pl-c1">Person</span> <span class="pl-k">=</span> {

  name<span class="pl-k">:</span> <span class="pl-s"><span class="pl-pds">'</span>张三<span class="pl-pds">'</span></span>,

  <span class="pl-c"><span class="pl-c">//</span>等同于birth: birth</span>
  birth,

  <span class="pl-c"><span class="pl-c">//</span> 等同于hello: function ()...</span>
  <span class="pl-en">hello</span>() { <span class="pl-en">console</span>.<span class="pl-c1">log</span>(<span class="pl-s"><span class="pl-pds">'</span>我的名字是<span class="pl-pds">'</span></span>, <span class="pl-c1">this</span>.<span class="pl-c1">name</span>); }

};</pre></div>
    <p>这种写法用于函数的返回值，将会非常方便。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">function</span> <span class="pl-en">getPoint</span>() {
  <span class="pl-k">const</span> <span class="pl-c1">x</span> <span class="pl-k">=</span> <span class="pl-c1">1</span>;
  <span class="pl-k">const</span> <span class="pl-c1">y</span> <span class="pl-k">=</span> <span class="pl-c1">10</span>;
  <span class="pl-k">return</span> {x, y};
}

<span class="pl-en">getPoint</span>()
<span class="pl-c"><span class="pl-c">//</span> {x:1, y:10}</span></pre></div>
    <p>CommonJS 模块输出一组变量，就非常合适使用简洁写法。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">let</span> ms <span class="pl-k">=</span> {};

<span class="pl-k">function</span> <span class="pl-en">getItem</span> (<span class="pl-smi">key</span>) {
  <span class="pl-k">return</span> key <span class="pl-k">in</span> ms <span class="pl-k">?</span> ms[key] <span class="pl-k">:</span> <span class="pl-c1">null</span>;
}

<span class="pl-k">function</span> <span class="pl-en">setItem</span> (<span class="pl-smi">key</span>, <span class="pl-smi">value</span>) {
  ms[key] <span class="pl-k">=</span> value;
}

<span class="pl-k">function</span> <span class="pl-en">clear</span> () {
  ms <span class="pl-k">=</span> {};
}

<span class="pl-c1">module</span>.<span class="pl-smi">exports</span> <span class="pl-k">=</span> { getItem, setItem, clear };
<span class="pl-c"><span class="pl-c">//</span> 等同于</span>
<span class="pl-c1">module</span>.<span class="pl-smi">exports</span> <span class="pl-k">=</span> {
  getItem<span class="pl-k">:</span> getItem,
  setItem<span class="pl-k">:</span> setItem,
  clear<span class="pl-k">:</span> clear
};</pre></div>
    <p>属性的赋值器（setter）和取值器（getter），事实上也是采用这种写法。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">const</span> <span class="pl-c1">cart</span> <span class="pl-k">=</span> {
  _wheels<span class="pl-k">:</span> <span class="pl-c1">4</span>,

  <span class="pl-k">get</span> <span class="pl-en">wheels</span> () {
    <span class="pl-k">return</span> <span class="pl-c1">this</span>.<span class="pl-smi">_wheels</span>;
  },

  <span class="pl-k">set</span> <span class="pl-en">wheels</span> (<span class="pl-smi">value</span>) {
    <span class="pl-k">if</span> (value <span class="pl-k">&lt;</span> <span class="pl-c1">this</span>.<span class="pl-smi">_wheels</span>) {
      <span class="pl-k">throw</span> <span class="pl-k">new</span> <span class="pl-en">Error</span>(<span class="pl-s"><span class="pl-pds">'</span>数值太小了！<span class="pl-pds">'</span></span>);
    }
    <span class="pl-c1">this</span>.<span class="pl-smi">_wheels</span> <span class="pl-k">=</span> value;
  }
}</pre></div>
    <p>注意，简洁写法的属性名总是字符串，这会导致一些看上去比较奇怪的结果。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">const</span> <span class="pl-c1">obj</span> <span class="pl-k">=</span> {
  <span class="pl-en">class</span> () {}
};

<span class="pl-c"><span class="pl-c">//</span> 等同于</span>

<span class="pl-k">var</span> obj <span class="pl-k">=</span> {
  <span class="pl-s"><span class="pl-pds">'</span><span class="pl-en">class</span><span class="pl-pds">'</span></span><span class="pl-k">:</span> <span class="pl-k">function</span>() {}
};</pre></div>
    <p>上面代码中，<code>class</code>是字符串，所以不会因为它属于关键字，而导致语法解析报错。</p>
    <p>如果某个方法的值是一个 Generator 函数，前面需要加上星号。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">const</span> <span class="pl-c1">obj</span> <span class="pl-k">=</span> {
  <span class="pl-k">*</span> <span class="pl-en">m</span>() {
    <span class="pl-k">yield</span> <span class="pl-s"><span class="pl-pds">'</span>hello world<span class="pl-pds">'</span></span>;
  }
};</pre></div>
    <h2><a href="#属性名表达式"  class="anchor" id="user-content-属性名表达式"><svg  class="octicon octicon-link" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>属性名表达式</h2>
    <p>JavaScript 定义对象的属性，有两种方法。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-c"><span class="pl-c">//</span> 方法一</span>
<span class="pl-smi">obj</span>.<span class="pl-smi">foo</span> <span class="pl-k">=</span> <span class="pl-c1">true</span>;

<span class="pl-c"><span class="pl-c">//</span> 方法二</span>
obj[<span class="pl-s"><span class="pl-pds">'</span>a<span class="pl-pds">'</span></span> <span class="pl-k">+</span> <span class="pl-s"><span class="pl-pds">'</span>bc<span class="pl-pds">'</span></span>] <span class="pl-k">=</span> <span class="pl-c1">123</span>;</pre></div>
    <p>上面代码的方法一是直接用标识符作为属性名，方法二是用表达式作为属性名，这时要将表达式放在方括号之内。</p>
    <p>但是，如果使用字面量方式定义对象（使用大括号），在 ES5 中只能使用方法一（标识符）定义属性。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">var</span> obj <span class="pl-k">=</span> {
  foo<span class="pl-k">:</span> <span class="pl-c1">true</span>,
  abc<span class="pl-k">:</span> <span class="pl-c1">123</span>
};</pre></div>
    <p>ES6 允许字面量定义对象时，用方法二（表达式）作为对象的属性名，即把表达式放在方括号内。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">let</span> propKey <span class="pl-k">=</span> <span class="pl-s"><span class="pl-pds">'</span>foo<span class="pl-pds">'</span></span>;

<span class="pl-k">let</span> obj <span class="pl-k">=</span> {
  [propKey]<span class="pl-k">:</span> <span class="pl-c1">true</span>,
  [<span class="pl-s"><span class="pl-pds">'</span>a<span class="pl-pds">'</span></span> <span class="pl-k">+</span> <span class="pl-s"><span class="pl-pds">'</span>bc<span class="pl-pds">'</span></span>]<span class="pl-k">:</span> <span class="pl-c1">123</span>
};</pre></div>
    <p>下面是另一个例子。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">let</span> lastWord <span class="pl-k">=</span> <span class="pl-s"><span class="pl-pds">'</span>last word<span class="pl-pds">'</span></span>;

<span class="pl-k">const</span> <span class="pl-c1">a</span> <span class="pl-k">=</span> {
  <span class="pl-s"><span class="pl-pds">'</span>first word<span class="pl-pds">'</span></span><span class="pl-k">:</span> <span class="pl-s"><span class="pl-pds">'</span>hello<span class="pl-pds">'</span></span>,
  [lastWord]<span class="pl-k">:</span> <span class="pl-s"><span class="pl-pds">'</span>world<span class="pl-pds">'</span></span>
};

a[<span class="pl-s"><span class="pl-pds">'</span>first word<span class="pl-pds">'</span></span>] <span class="pl-c"><span class="pl-c">//</span> "hello"</span>
a[lastWord] <span class="pl-c"><span class="pl-c">//</span> "world"</span>
a[<span class="pl-s"><span class="pl-pds">'</span>last word<span class="pl-pds">'</span></span>] <span class="pl-c"><span class="pl-c">//</span> "world"</span></pre></div>
    <p>表达式还可以用于定义方法名。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">let</span> obj <span class="pl-k">=</span> {
  [<span class="pl-s"><span class="pl-pds">'</span>h<span class="pl-pds">'</span></span> <span class="pl-k">+</span> <span class="pl-s"><span class="pl-pds">'</span>ello<span class="pl-pds">'</span></span>]() {
    <span class="pl-k">return</span> <span class="pl-s"><span class="pl-pds">'</span>hi<span class="pl-pds">'</span></span>;
  }
};

<span class="pl-smi">obj</span>.<span class="pl-en">hello</span>() <span class="pl-c"><span class="pl-c">//</span> hi</span></pre></div>
    <p>注意，属性名表达式与简洁表示法，不能同时使用，会报错。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-c"><span class="pl-c">//</span> 报错</span>
<span class="pl-k">const</span> <span class="pl-c1">foo</span> <span class="pl-k">=</span> <span class="pl-s"><span class="pl-pds">'</span>bar<span class="pl-pds">'</span></span>;
<span class="pl-k">const</span> <span class="pl-c1">bar</span> <span class="pl-k">=</span> <span class="pl-s"><span class="pl-pds">'</span>abc<span class="pl-pds">'</span></span>;
<span class="pl-k">const</span> <span class="pl-c1">baz</span> <span class="pl-k">=</span> { [foo] };

<span class="pl-c"><span class="pl-c">//</span> 正确</span>
<span class="pl-k">const</span> <span class="pl-c1">foo</span> <span class="pl-k">=</span> <span class="pl-s"><span class="pl-pds">'</span>bar<span class="pl-pds">'</span></span>;
<span class="pl-k">const</span> <span class="pl-c1">baz</span> <span class="pl-k">=</span> { [foo]<span class="pl-k">:</span> <span class="pl-s"><span class="pl-pds">'</span>abc<span class="pl-pds">'</span></span>};</pre></div>
    <p>注意，属性名表达式如果是一个对象，默认情况下会自动将对象转为字符串<code>[object Object]</code>，这一点要特别小心。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">const</span> <span class="pl-c1">keyA</span> <span class="pl-k">=</span> {a<span class="pl-k">:</span> <span class="pl-c1">1</span>};
<span class="pl-k">const</span> <span class="pl-c1">keyB</span> <span class="pl-k">=</span> {b<span class="pl-k">:</span> <span class="pl-c1">2</span>};

<span class="pl-k">const</span> <span class="pl-c1">myObject</span> <span class="pl-k">=</span> {
  [keyA]<span class="pl-k">:</span> <span class="pl-s"><span class="pl-pds">'</span>valueA<span class="pl-pds">'</span></span>,
  [keyB]<span class="pl-k">:</span> <span class="pl-s"><span class="pl-pds">'</span>valueB<span class="pl-pds">'</span></span>
};

myObject <span class="pl-c"><span class="pl-c">//</span> Object {[object Object]: "valueB"}</span></pre></div>
    <p>上面代码中，<code>[keyA]</code>和<code>[keyB]</code>得到的都是<code>[object Object]</code>，所以<code>[keyB]</code>会把<code>[keyA]</code>覆盖掉，而<code>myObject</code>最后只有一个<code>[object Object]</code>属性。</p>
    <h2><a href="#方法的-name-属性"  class="anchor" id="user-content-方法的-name-属性"><svg  class="octicon octicon-link" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>方法的 name 属性</h2>
    <p>函数的<code>name</code>属性，返回函数名。对象方法也是函数，因此也有<code>name</code>属性。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">const</span> <span class="pl-c1">person</span> <span class="pl-k">=</span> {
  <span class="pl-en">sayName</span>() {
 &nbsp; &nbsp;<span class="pl-en">console</span>.<span class="pl-c1">log</span>(<span class="pl-s"><span class="pl-pds">'</span>hello!<span class="pl-pds">'</span></span>);
  },
};

<span class="pl-smi">person</span>.<span class="pl-smi">sayName</span>.<span class="pl-c1">name</span>   <span class="pl-c"><span class="pl-c">//</span> "sayName"</span></pre></div>
    <p>上面代码中，方法的<code>name</code>属性返回函数名（即方法名）。</p>
    <p>如果对象的方法使用了取值函数（<code>getter</code>）和存值函数（<code>setter</code>），则<code>name</code>属性不是在该方法上面，而是该方法的属性的描述对象的<code>get</code>和<code>set</code>属性上面，返回值是方法名前加上<code>get</code>和<code>set</code>。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">const</span> <span class="pl-c1">obj</span> <span class="pl-k">=</span> {
  <span class="pl-k">get</span> <span class="pl-en">foo</span>() {},
  <span class="pl-k">set</span> <span class="pl-en">foo</span>(<span class="pl-smi">x</span>) {}
};

<span class="pl-smi">obj</span>.<span class="pl-smi">foo</span>.<span class="pl-c1">name</span>
<span class="pl-c"><span class="pl-c">//</span> TypeError: Cannot read property 'name' of undefined</span>

<span class="pl-k">const</span> <span class="pl-c1">descriptor</span> <span class="pl-k">=</span> <span class="pl-c1">Object</span>.<span class="pl-en">getOwnPropertyDescriptor</span>(obj, <span class="pl-s"><span class="pl-pds">'</span>foo<span class="pl-pds">'</span></span>);

<span class="pl-smi">descriptor</span>.<span class="pl-smi">get</span>.<span class="pl-c1">name</span> <span class="pl-c"><span class="pl-c">//</span> "get foo"</span>
<span class="pl-smi">descriptor</span>.<span class="pl-smi">set</span>.<span class="pl-c1">name</span> <span class="pl-c"><span class="pl-c">//</span> "set foo"</span></pre></div>
    <p>有两种特殊情况：<code>bind</code>方法创造的函数，<code>name</code>属性返回<code>bound</code>加上原函数的名字；<code>Function</code>构造函数创造的函数，<code>name</code>属性返回<code>anonymous</code>。</p>
    <div class="highlight highlight-source-js"><pre>(<span class="pl-k">new</span> <span class="pl-en">Function</span>()).<span class="pl-c1">name</span> <span class="pl-c"><span class="pl-c">//</span> "anonymous"</span>

<span class="pl-k">var</span> <span class="pl-en">doSomething</span> <span class="pl-k">=</span> <span class="pl-k">function</span>() {
  <span class="pl-c"><span class="pl-c">//</span> ...</span>
};
<span class="pl-smi">doSomething</span>.<span class="pl-en">bind</span>().<span class="pl-c1">name</span> <span class="pl-c"><span class="pl-c">//</span> "bound doSomething"</span></pre></div>
    <p>如果对象的方法是一个 Symbol 值，那么<code>name</code>属性返回的是这个 Symbol 值的描述。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">const</span> <span class="pl-c1">key1</span> <span class="pl-k">=</span> <span class="pl-c1">Symbol</span>(<span class="pl-s"><span class="pl-pds">'</span>description<span class="pl-pds">'</span></span>);
<span class="pl-k">const</span> <span class="pl-c1">key2</span> <span class="pl-k">=</span> <span class="pl-c1">Symbol</span>();
<span class="pl-k">let</span> obj <span class="pl-k">=</span> {
  [<span class="pl-v">key1</span>]() {},
  [<span class="pl-v">key2</span>]() {},
};
obj[key1].<span class="pl-c1">name</span> <span class="pl-c"><span class="pl-c">//</span> "[description]"</span>
obj[key2].<span class="pl-c1">name</span> <span class="pl-c"><span class="pl-c">//</span> ""</span></pre></div>
    <p>上面代码中，<code>key1</code>对应的 Symbol 值有描述，<code>key2</code>没有。</p>
    <h2><a href="#objectis"  class="anchor" id="user-content-objectis"><svg  class="octicon octicon-link" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Object.is()</h2>
    <p>ES5 比较两个值是否相等，只有两个运算符：相等运算符（<code>==</code>）和严格相等运算符（<code>===</code>）。它们都有缺点，前者会自动转换数据类型，后者的<code>NaN</code>不等于自身，以及<code>+0</code>等于<code>-0</code>。JavaScript 缺乏一种运算，在所有环境中，只要两个值是一样的，它们就应该相等。</p>
    <p>ES6 提出“Same-value equality”（同值相等）算法，用来解决这个问题。<code>Object.is</code>就是部署这个算法的新方法。它用来比较两个值是否严格相等，与严格比较运算符（===）的行为基本一致。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-c1">Object</span>.<span class="pl-en">is</span>(<span class="pl-s"><span class="pl-pds">'</span>foo<span class="pl-pds">'</span></span>, <span class="pl-s"><span class="pl-pds">'</span>foo<span class="pl-pds">'</span></span>)
<span class="pl-c"><span class="pl-c">//</span> true</span>
<span class="pl-c1">Object</span>.<span class="pl-en">is</span>({}, {})
<span class="pl-c"><span class="pl-c">//</span> false</span></pre></div>
    <p>不同之处只有两个：一是<code>+0</code>不等于<code>-0</code>，二是<code>NaN</code>等于自身。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">+</span><span class="pl-c1">0</span> <span class="pl-k">===</span> <span class="pl-k">-</span><span class="pl-c1">0</span> <span class="pl-c"><span class="pl-c">//</span>true</span>
<span class="pl-c1">NaN</span> <span class="pl-k">===</span> <span class="pl-c1">NaN</span> <span class="pl-c"><span class="pl-c">//</span> false</span>

<span class="pl-c1">Object</span>.<span class="pl-en">is</span>(<span class="pl-k">+</span><span class="pl-c1">0</span>, <span class="pl-k">-</span><span class="pl-c1">0</span>) <span class="pl-c"><span class="pl-c">//</span> false</span>
<span class="pl-c1">Object</span>.<span class="pl-en">is</span>(<span class="pl-c1">NaN</span>, <span class="pl-c1">NaN</span>) <span class="pl-c"><span class="pl-c">//</span> true</span></pre></div>
    <p>ES5 可以通过下面的代码，部署<code>Object.is</code>。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-c1">Object</span>.<span class="pl-en">defineProperty</span>(<span class="pl-c1">Object</span>, <span class="pl-s"><span class="pl-pds">'</span>is<span class="pl-pds">'</span></span>, {
  <span class="pl-en">value</span><span class="pl-k">:</span> <span class="pl-k">function</span>(<span class="pl-smi">x</span>, <span class="pl-smi">y</span>) {
    <span class="pl-k">if</span> (x <span class="pl-k">===</span> y) {
      <span class="pl-c"><span class="pl-c">//</span> 针对+0 不等于 -0的情况</span>
      <span class="pl-k">return</span> x <span class="pl-k">!==</span> <span class="pl-c1">0</span> <span class="pl-k">||</span> <span class="pl-c1">1</span> <span class="pl-k">/</span> x <span class="pl-k">===</span> <span class="pl-c1">1</span> <span class="pl-k">/</span> y;
    }
    <span class="pl-c"><span class="pl-c">//</span> 针对NaN的情况</span>
    <span class="pl-k">return</span> x <span class="pl-k">!==</span> x <span class="pl-k">&amp;&amp;</span> y <span class="pl-k">!==</span> y;
  },
  configurable<span class="pl-k">:</span> <span class="pl-c1">true</span>,
  enumerable<span class="pl-k">:</span> <span class="pl-c1">false</span>,
  writable<span class="pl-k">:</span> <span class="pl-c1">true</span>
});</pre></div>
    <h2><a href="#objectassign"  class="anchor" id="user-content-objectassign"><svg  class="octicon octicon-link" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Object.assign()</h2>
    <h3><a href="#基本用法"  class="anchor" id="user-content-基本用法"><svg  class="octicon octicon-link" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>基本用法</h3>
    <p><code>Object.assign</code>方法用于对象的合并，将源对象（source）的所有可枚举属性，复制到目标对象（target）。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">const</span> <span class="pl-c1">target</span> <span class="pl-k">=</span> { a<span class="pl-k">:</span> <span class="pl-c1">1</span> };

<span class="pl-k">const</span> <span class="pl-c1">source1</span> <span class="pl-k">=</span> { b<span class="pl-k">:</span> <span class="pl-c1">2</span> };
<span class="pl-k">const</span> <span class="pl-c1">source2</span> <span class="pl-k">=</span> { c<span class="pl-k">:</span> <span class="pl-c1">3</span> };

<span class="pl-c1">Object</span>.<span class="pl-en">assign</span>(target, source1, source2);
target <span class="pl-c"><span class="pl-c">//</span> {a:1, b:2, c:3}</span></pre></div>
    <p><code>Object.assign</code>方法的第一个参数是目标对象，后面的参数都是源对象。</p>
    <p>注意，如果目标对象与源对象有同名属性，或多个源对象有同名属性，则后面的属性会覆盖前面的属性。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">const</span> <span class="pl-c1">target</span> <span class="pl-k">=</span> { a<span class="pl-k">:</span> <span class="pl-c1">1</span>, b<span class="pl-k">:</span> <span class="pl-c1">1</span> };

<span class="pl-k">const</span> <span class="pl-c1">source1</span> <span class="pl-k">=</span> { b<span class="pl-k">:</span> <span class="pl-c1">2</span>, c<span class="pl-k">:</span> <span class="pl-c1">2</span> };
<span class="pl-k">const</span> <span class="pl-c1">source2</span> <span class="pl-k">=</span> { c<span class="pl-k">:</span> <span class="pl-c1">3</span> };

<span class="pl-c1">Object</span>.<span class="pl-en">assign</span>(target, source1, source2);
target <span class="pl-c"><span class="pl-c">//</span> {a:1, b:2, c:3}</span></pre></div>
    <p>如果只有一个参数，<code>Object.assign</code>会直接返回该参数。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">const</span> <span class="pl-c1">obj</span> <span class="pl-k">=</span> {a<span class="pl-k">:</span> <span class="pl-c1">1</span>};
<span class="pl-c1">Object</span>.<span class="pl-en">assign</span>(obj) <span class="pl-k">===</span> obj <span class="pl-c"><span class="pl-c">//</span> true</span></pre></div>
    <p>如果该参数不是对象，则会先转成对象，然后返回。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">typeof</span> <span class="pl-c1">Object</span>.<span class="pl-en">assign</span>(<span class="pl-c1">2</span>) <span class="pl-c"><span class="pl-c">//</span> "object"</span></pre></div>
    <p>由于<code>undefined</code>和<code>null</code>无法转成对象，所以如果它们作为参数，就会报错。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-c1">Object</span>.<span class="pl-en">assign</span>(<span class="pl-c1">undefined</span>) <span class="pl-c"><span class="pl-c">//</span> 报错</span>
<span class="pl-c1">Object</span>.<span class="pl-en">assign</span>(<span class="pl-c1">null</span>) <span class="pl-c"><span class="pl-c">//</span> 报错</span></pre></div>
    <p>如果非对象参数出现在源对象的位置（即非首参数），那么处理规则有所不同。首先，这些参数都会转成对象，如果无法转成对象，就会跳过。这意味着，如果<code>undefined</code>和<code>null</code>不在首参数，就不会报错。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">let</span> obj <span class="pl-k">=</span> {a<span class="pl-k">:</span> <span class="pl-c1">1</span>};
<span class="pl-c1">Object</span>.<span class="pl-en">assign</span>(obj, <span class="pl-c1">undefined</span>) <span class="pl-k">===</span> obj <span class="pl-c"><span class="pl-c">//</span> true</span>
<span class="pl-c1">Object</span>.<span class="pl-en">assign</span>(obj, <span class="pl-c1">null</span>) <span class="pl-k">===</span> obj <span class="pl-c"><span class="pl-c">//</span> true</span></pre></div>
    <p>其他类型的值（即数值、字符串和布尔值）不在首参数，也不会报错。但是，除了字符串会以数组形式，拷贝入目标对象，其他值都不会产生效果。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">const</span> <span class="pl-c1">v1</span> <span class="pl-k">=</span> <span class="pl-s"><span class="pl-pds">'</span>abc<span class="pl-pds">'</span></span>;
<span class="pl-k">const</span> <span class="pl-c1">v2</span> <span class="pl-k">=</span> <span class="pl-c1">true</span>;
<span class="pl-k">const</span> <span class="pl-c1">v3</span> <span class="pl-k">=</span> <span class="pl-c1">10</span>;

<span class="pl-k">const</span> <span class="pl-c1">obj</span> <span class="pl-k">=</span> <span class="pl-c1">Object</span>.<span class="pl-en">assign</span>({}, v1, v2, v3);
<span class="pl-en">console</span>.<span class="pl-c1">log</span>(obj); <span class="pl-c"><span class="pl-c">//</span> { "0": "a", "1": "b", "2": "c" }</span></pre></div>
    <p>上面代码中，<code>v1</code>、<code>v2</code>、<code>v3</code>分别是字符串、布尔值和数值，结果只有字符串合入目标对象（以字符数组的形式），数值和布尔值都会被忽略。这是因为只有字符串的包装对象，会产生可枚举属性。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-c1">Object</span>(<span class="pl-c1">true</span>) <span class="pl-c"><span class="pl-c">//</span> {[[PrimitiveValue]]: true}</span>
<span class="pl-c1">Object</span>(<span class="pl-c1">10</span>)  <span class="pl-c"><span class="pl-c">//</span>  {[[PrimitiveValue]]: 10}</span>
<span class="pl-c1">Object</span>(<span class="pl-s"><span class="pl-pds">'</span>abc<span class="pl-pds">'</span></span>) <span class="pl-c"><span class="pl-c">//</span> {0: "a", 1: "b", 2: "c", length: 3, [[PrimitiveValue]]: "abc"}</span></pre></div>
    <p>上面代码中，布尔值、数值、字符串分别转成对应的包装对象，可以看到它们的原始值都在包装对象的内部属性<code>[[PrimitiveValue]]</code>上面，这个属性是不会被<code>Object.assign</code>拷贝的。只有字符串的包装对象，会产生可枚举的实义属性，那些属性则会被拷贝。</p>
    <p><code>Object.assign</code>拷贝的属性是有限制的，只拷贝源对象的自身属性（不拷贝继承属性），也不拷贝不可枚举的属性（<code>enumerable: false</code>）。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-c1">Object</span>.<span class="pl-en">assign</span>({b<span class="pl-k">:</span> <span class="pl-s"><span class="pl-pds">'</span>c<span class="pl-pds">'</span></span>},
  <span class="pl-c1">Object</span>.<span class="pl-en">defineProperty</span>({}, <span class="pl-s"><span class="pl-pds">'</span>invisible<span class="pl-pds">'</span></span>, {
    enumerable<span class="pl-k">:</span> <span class="pl-c1">false</span>,
    value<span class="pl-k">:</span> <span class="pl-s"><span class="pl-pds">'</span>hello<span class="pl-pds">'</span></span>
  })
)
<span class="pl-c"><span class="pl-c">//</span> { b: 'c' }</span></pre></div>
    <p>上面代码中，<code>Object.assign</code>要拷贝的对象只有一个不可枚举属性<code>invisible</code>，这个属性并没有被拷贝进去。</p>
    <p>属性名为 Symbol 值的属性，也会被<code>Object.assign</code>拷贝。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-c1">Object</span>.<span class="pl-en">assign</span>({ a<span class="pl-k">:</span> <span class="pl-s"><span class="pl-pds">'</span>b<span class="pl-pds">'</span></span> }, { [<span class="pl-c1">Symbol</span>(<span class="pl-s"><span class="pl-pds">'</span>c<span class="pl-pds">'</span></span>)]<span class="pl-k">:</span> <span class="pl-s"><span class="pl-pds">'</span>d<span class="pl-pds">'</span></span> })
<span class="pl-c"><span class="pl-c">//</span> { a: 'b', Symbol(c): 'd' }</span></pre></div>
    <h3><a href="#注意点"  class="anchor" id="user-content-注意点"><svg  class="octicon octicon-link" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>注意点</h3>
    <p><strong>（1）浅拷贝</strong></p>
    <p><code>Object.assign</code>方法实行的是浅拷贝，而不是深拷贝。也就是说，如果源对象某个属性的值是对象，那么目标对象拷贝得到的是这个对象的引用。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">const</span> <span class="pl-c1">obj1</span> <span class="pl-k">=</span> {a<span class="pl-k">:</span> {b<span class="pl-k">:</span> <span class="pl-c1">1</span>}};
<span class="pl-k">const</span> <span class="pl-c1">obj2</span> <span class="pl-k">=</span> <span class="pl-c1">Object</span>.<span class="pl-en">assign</span>({}, obj1);

<span class="pl-smi">obj1</span>.<span class="pl-smi">a</span>.<span class="pl-smi">b</span> <span class="pl-k">=</span> <span class="pl-c1">2</span>;
<span class="pl-smi">obj2</span>.<span class="pl-smi">a</span>.<span class="pl-smi">b</span> <span class="pl-c"><span class="pl-c">//</span> 2</span></pre></div>
    <p>上面代码中，源对象<code>obj1</code>的<code>a</code>属性的值是一个对象，<code>Object.assign</code>拷贝得到的是这个对象的引用。这个对象的任何变化，都会反映到目标对象上面。</p>
    <p><strong>（2）同名属性的替换</strong></p>
    <p>对于这种嵌套的对象，一旦遇到同名属性，<code>Object.assign</code>的处理方法是替换，而不是添加。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">const</span> <span class="pl-c1">target</span> <span class="pl-k">=</span> { a<span class="pl-k">:</span> { b<span class="pl-k">:</span> <span class="pl-s"><span class="pl-pds">'</span>c<span class="pl-pds">'</span></span>, d<span class="pl-k">:</span> <span class="pl-s"><span class="pl-pds">'</span>e<span class="pl-pds">'</span></span> } }
<span class="pl-k">const</span> <span class="pl-c1">source</span> <span class="pl-k">=</span> { a<span class="pl-k">:</span> { b<span class="pl-k">:</span> <span class="pl-s"><span class="pl-pds">'</span>hello<span class="pl-pds">'</span></span> } }
<span class="pl-c1">Object</span>.<span class="pl-en">assign</span>(target, source)
<span class="pl-c"><span class="pl-c">//</span> { a: { b: 'hello' } }</span></pre></div>
    <p>上面代码中，<code>target</code>对象的<code>a</code>属性被<code>source</code>对象的<code>a</code>属性整个替换掉了，而不会得到<code>{ a: { b: 'hello', d: 'e' } }</code>的结果。这通常不是开发者想要的，需要特别小心。</p>
    <p>一些函数库提供<code>Object.assign</code>的定制版本（比如 Lodash 的<code>_.defaultsDeep</code>方法），可以得到深拷贝的合并。</p>
    <p><strong>（3）数组的处理</strong></p>
    <p><code>Object.assign</code>可以用来处理数组，但是会把数组视为对象。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-c1">Object</span>.<span class="pl-en">assign</span>([<span class="pl-c1">1</span>, <span class="pl-c1">2</span>, <span class="pl-c1">3</span>], [<span class="pl-c1">4</span>, <span class="pl-c1">5</span>])
<span class="pl-c"><span class="pl-c">//</span> [4, 5, 3]</span></pre></div>
    <p>上面代码中，<code>Object.assign</code>把数组视为属性名为 0、1、2 的对象，因此源数组的 0 号属性<code>4</code>覆盖了目标数组的 0 号属性<code>1</code>。</p>
    <p><strong>（4）取值函数的处理</strong></p>
    <p><code>Object.assign</code>只能进行值的复制，如果要复制的值是一个取值函数，那么将求值后再复制。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">const</span> <span class="pl-c1">source</span> <span class="pl-k">=</span> {
  <span class="pl-k">get</span> <span class="pl-en">foo</span>() { <span class="pl-k">return</span> <span class="pl-c1">1</span> }
};
<span class="pl-k">const</span> <span class="pl-c1">target</span> <span class="pl-k">=</span> {};

<span class="pl-c1">Object</span>.<span class="pl-en">assign</span>(target, source)
<span class="pl-c"><span class="pl-c">//</span> { foo: 1 }</span></pre></div>
    <p>上面代码中，<code>source</code>对象的<code>foo</code>属性是一个取值函数，<code>Object.assign</code>不会复制这个取值函数，只会拿到值以后，将这个值复制过去。</p>
    <h3><a href="#常见用途"  class="anchor" id="user-content-常见用途"><svg  class="octicon octicon-link" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>常见用途</h3>
    <p><code>Object.assign</code>方法有很多用处。</p>
    <p><strong>（1）为对象添加属性</strong></p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">class</span> <span class="pl-en">Point</span> {
  <span class="pl-en">constructor</span>(<span class="pl-smi">x</span>, <span class="pl-smi">y</span>) {
    <span class="pl-c1">Object</span>.<span class="pl-en">assign</span>(<span class="pl-c1">this</span>, {x, y});
  }
}</pre></div>
    <p>上面方法通过<code>Object.assign</code>方法，将<code>x</code>属性和<code>y</code>属性添加到<code>Point</code>类的对象实例。</p>
    <p><strong>（2）为对象添加方法</strong></p>
    <div class="highlight highlight-source-js"><pre><span class="pl-c1">Object</span>.<span class="pl-en">assign</span>(<span class="pl-smi">SomeClass</span>.<span class="pl-c1">prototype</span>, {
  <span class="pl-en">someMethod</span>(<span class="pl-smi">arg1</span>, <span class="pl-smi">arg2</span>) {
    ···
  },
  <span class="pl-en">anotherMethod</span>() {
    ···
  }
});

<span class="pl-c"><span class="pl-c">//</span> 等同于下面的写法</span>
<span class="pl-smi">SomeClass</span>.<span class="pl-c1">prototype</span>.<span class="pl-en">someMethod</span> <span class="pl-k">=</span> <span class="pl-k">function</span> (<span class="pl-smi">arg1</span>, <span class="pl-smi">arg2</span>) {
  ···
};
<span class="pl-smi">SomeClass</span>.<span class="pl-c1">prototype</span>.<span class="pl-en">anotherMethod</span> <span class="pl-k">=</span> <span class="pl-k">function</span> () {
  ···
};</pre></div>
    <p>上面代码使用了对象属性的简洁表示法，直接将两个函数放在大括号中，再使用<code>assign</code>方法添加到<code>SomeClass.prototype</code>之中。</p>
    <p><strong>（3）克隆对象</strong></p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">function</span> <span class="pl-en">clone</span>(<span class="pl-smi">origin</span>) {
  <span class="pl-k">return</span> <span class="pl-c1">Object</span>.<span class="pl-en">assign</span>({}, origin);
}</pre></div>
    <p>上面代码将原始对象拷贝到一个空对象，就得到了原始对象的克隆。</p>
    <p>不过，采用这种方法克隆，只能克隆原始对象自身的值，不能克隆它继承的值。如果想要保持继承链，可以采用下面的代码。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">function</span> <span class="pl-en">clone</span>(<span class="pl-smi">origin</span>) {
  <span class="pl-k">let</span> originProto <span class="pl-k">=</span> <span class="pl-c1">Object</span>.<span class="pl-en">getPrototypeOf</span>(origin);
  <span class="pl-k">return</span> <span class="pl-c1">Object</span>.<span class="pl-en">assign</span>(<span class="pl-c1">Object</span>.<span class="pl-en">create</span>(originProto), origin);
}</pre></div>
    <p><strong>（4）合并多个对象</strong></p>
    <p>将多个对象合并到某个对象。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">const</span> <span class="pl-c1">merge</span> <span class="pl-k">=</span>
  (<span class="pl-smi">target</span>, <span class="pl-k">...</span><span class="pl-v">sources</span>) <span class="pl-k">=&gt;</span> <span class="pl-c1">Object</span>.<span class="pl-en">assign</span>(target, <span class="pl-k">...</span>sources);</pre></div>
    <p>如果希望合并后返回一个新对象，可以改写上面函数，对一个空对象合并。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">const</span> <span class="pl-c1">merge</span> <span class="pl-k">=</span>
  (<span class="pl-k">...</span><span class="pl-v">sources</span>) <span class="pl-k">=&gt;</span> <span class="pl-c1">Object</span>.<span class="pl-en">assign</span>({}, <span class="pl-k">...</span>sources);</pre></div>
    <p><strong>（5）为属性指定默认值</strong></p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">const</span> <span class="pl-c1">DEFAULTS</span> <span class="pl-k">=</span> {
  logLevel<span class="pl-k">:</span> <span class="pl-c1">0</span>,
  outputFormat<span class="pl-k">:</span> <span class="pl-s"><span class="pl-pds">'</span>html<span class="pl-pds">'</span></span>
};

<span class="pl-k">function</span> <span class="pl-en">processContent</span>(<span class="pl-smi">options</span>) {
  options <span class="pl-k">=</span> <span class="pl-c1">Object</span>.<span class="pl-en">assign</span>({}, <span class="pl-c1">DEFAULTS</span>, options);
  <span class="pl-en">console</span>.<span class="pl-c1">log</span>(options);
  <span class="pl-c"><span class="pl-c">//</span> ...</span>
}</pre></div>
    <p>上面代码中，<code>DEFAULTS</code>对象是默认值，<code>options</code>对象是用户提供的参数。<code>Object.assign</code>方法将<code>DEFAULTS</code>和<code>options</code>合并成一个新对象，如果两者有同名属性，则<code>option</code>的属性值会覆盖<code>DEFAULTS</code>的属性值。</p>
    <p>注意，由于存在浅拷贝的问题，<code>DEFAULTS</code>对象和<code>options</code>对象的所有属性的值，最好都是简单类型，不要指向另一个对象。否则，<code>DEFAULTS</code>对象的该属性很可能不起作用。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">const</span> <span class="pl-c1">DEFAULTS</span> <span class="pl-k">=</span> {
  url<span class="pl-k">:</span> {
    host<span class="pl-k">:</span> <span class="pl-s"><span class="pl-pds">'</span>example.com<span class="pl-pds">'</span></span>,
    port<span class="pl-k">:</span> <span class="pl-c1">7070</span>
  },
};

<span class="pl-en">processContent</span>({ url<span class="pl-k">:</span> {port<span class="pl-k">:</span> <span class="pl-c1">8000</span>} })
<span class="pl-c"><span class="pl-c">//</span> {</span>
<span class="pl-c"><span class="pl-c">//</span>   url: {port: 8000}</span>
<span class="pl-c"><span class="pl-c">//</span> }</span></pre></div>
    <p>上面代码的原意是将<code>url.port</code>改成 8000，<code>url.host</code>不变。实际结果却是<code>options.url</code>覆盖掉<code>DEFAULTS.url</code>，所以<code>url.host</code>就不存在了。</p>
    <h2><a href="#属性的可枚举性和遍历"  class="anchor" id="user-content-属性的可枚举性和遍历"><svg  class="octicon octicon-link" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>属性的可枚举性和遍历</h2>
    <h3><a href="#可枚举性"  class="anchor" id="user-content-可枚举性"><svg  class="octicon octicon-link" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>可枚举性</h3>
    <p>对象的每个属性都有一个描述对象（Descriptor），用来控制该属性的行为。<code>Object.getOwnPropertyDescriptor</code>方法可以获取该属性的描述对象。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">let</span> obj <span class="pl-k">=</span> { foo<span class="pl-k">:</span> <span class="pl-c1">123</span> };
<span class="pl-c1">Object</span>.<span class="pl-en">getOwnPropertyDescriptor</span>(obj, <span class="pl-s"><span class="pl-pds">'</span>foo<span class="pl-pds">'</span></span>)
<span class="pl-c"><span class="pl-c">//</span>  {</span>
<span class="pl-c"><span class="pl-c">//</span>    value: 123,</span>
<span class="pl-c"><span class="pl-c">//</span>    writable: true,</span>
<span class="pl-c"><span class="pl-c">//</span>    enumerable: true,</span>
<span class="pl-c"><span class="pl-c">//</span>    configurable: true</span>
<span class="pl-c"><span class="pl-c">//</span>  }</span></pre></div>
    <p>描述对象的<code>enumerable</code>属性，称为”可枚举性“，如果该属性为<code>false</code>，就表示某些操作会忽略当前属性。</p>
    <p>目前，有四个操作会忽略<code>enumerable</code>为<code>false</code>的属性。</p>
    <ul>
        <li><code>for...in</code>循环：只遍历对象自身的和继承的可枚举的属性。</li>
        <li><code>Object.keys()</code>：返回对象自身的所有可枚举的属性的键名。</li>
        <li><code>JSON.stringify()</code>：只串行化对象自身的可枚举的属性。</li>
        <li><code>Object.assign()</code>： 忽略<code>enumerable</code>为<code>false</code>的属性，只拷贝对象自身的可枚举的属性。</li>
    </ul>
    <p>这四个操作之中，前三个是 ES5 就有的，最后一个<code>Object.assign()</code>是 ES6 新增的。其中，只有<code>for...in</code>会返回继承的属性，其他三个方法都会忽略继承的属性，只处理对象自身的属性。实际上，引入“可枚举”（<code>enumerable</code>）这个概念的最初目的，就是让某些属性可以规避掉<code>for...in</code>操作，不然所有内部属性和方法都会被遍历到。比如，对象原型的<code>toString</code>方法，以及数组的<code>length</code>属性，就通过“可枚举性”，从而避免被<code>for...in</code>遍历到。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-c1">Object</span>.<span class="pl-en">getOwnPropertyDescriptor</span>(<span class="pl-c1">Object</span>.<span class="pl-c1">prototype</span>, <span class="pl-s"><span class="pl-pds">'</span>toString<span class="pl-pds">'</span></span>).<span class="pl-smi">enumerable</span>
<span class="pl-c"><span class="pl-c">//</span> false</span>

<span class="pl-c1">Object</span>.<span class="pl-en">getOwnPropertyDescriptor</span>([], <span class="pl-s"><span class="pl-pds">'</span>length<span class="pl-pds">'</span></span>).<span class="pl-smi">enumerable</span>
<span class="pl-c"><span class="pl-c">//</span> false</span></pre></div>
    <p>上面代码中，<code>toString</code>和<code>length</code>属性的<code>enumerable</code>都是<code>false</code>，因此<code>for...in</code>不会遍历到这两个继承自原型的属性。</p>
    <p>另外，ES6 规定，所有 Class 的原型的方法都是不可枚举的。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-c1">Object</span>.<span class="pl-en">getOwnPropertyDescriptor</span>(<span class="pl-k">class</span> {<span class="pl-en">foo</span>() {}}.<span class="pl-c1">prototype</span>, <span class="pl-s"><span class="pl-pds">'</span>foo<span class="pl-pds">'</span></span>).<span class="pl-smi">enumerable</span>
<span class="pl-c"><span class="pl-c">//</span> false</span></pre></div>
    <p>总的来说，操作中引入继承的属性会让问题复杂化，大多数时候，我们只关心对象自身的属性。所以，尽量不要用<code>for...in</code>循环，而用<code>Object.keys()</code>代替。</p>
    <h3><a href="#属性的遍历"  class="anchor" id="user-content-属性的遍历"><svg  class="octicon octicon-link" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>属性的遍历</h3>
    <p>ES6 一共有 5 种方法可以遍历对象的属性。</p>
    <p><strong>（1）for...in</strong></p>
    <p><code>for...in</code>循环遍历对象自身的和继承的可枚举属性（不含 Symbol 属性）。</p>
    <p><strong>（2）Object.keys(obj)</strong></p>
    <p><code>Object.keys</code>返回一个数组，包括对象自身的（不含继承的）所有可枚举属性（不含 Symbol 属性）的键名。</p>
    <p><strong>（3）Object.getOwnPropertyNames(obj)</strong></p>
    <p><code>Object.getOwnPropertyNames</code>返回一个数组，包含对象自身的所有属性（不含 Symbol 属性，但是包括不可枚举属性）的键名。</p>
    <p><strong>（4）Object.getOwnPropertySymbols(obj)</strong></p>
    <p><code>Object.getOwnPropertySymbols</code>返回一个数组，包含对象自身的所有 Symbol 属性的键名。</p>
    <p><strong>（5）Reflect.ownKeys(obj)</strong></p>
    <p><code>Reflect.ownKeys</code>返回一个数组，包含对象自身的所有键名，不管键名是 Symbol 或字符串，也不管是否可枚举。</p>
    <p>以上的 5 种方法遍历对象的键名，都遵守同样的属性遍历的次序规则。</p>
    <ul>
        <li>首先遍历所有数值键，按照数值升序排列。</li>
        <li>其次遍历所有字符串键，按照加入时间升序排列。</li>
        <li>最后遍历所有 Symbol 键，按照加入时间升序排列。</li>
    </ul>
    <div class="highlight highlight-source-js"><pre><span class="pl-c1">Reflect</span>.<span class="pl-en">ownKeys</span>({ [<span class="pl-c1">Symbol</span>()]<span class="pl-k">:</span><span class="pl-c1">0</span>, b<span class="pl-k">:</span><span class="pl-c1">0</span>, <span class="pl-c1">10</span><span class="pl-k">:</span><span class="pl-c1">0</span>, <span class="pl-c1">2</span><span class="pl-k">:</span><span class="pl-c1">0</span>, a<span class="pl-k">:</span><span class="pl-c1">0</span> })
<span class="pl-c"><span class="pl-c">//</span> ['2', '10', 'b', 'a', Symbol()]</span></pre></div>
    <p>上面代码中，<code>Reflect.ownKeys</code>方法返回一个数组，包含了参数对象的所有属性。这个数组的属性次序是这样的，首先是数值属性<code>2</code>和<code>10</code>，其次是字符串属性<code>b</code>和<code>a</code>，最后是 Symbol 属性。</p>
    <h2><a href="#objectgetownpropertydescriptors"  class="anchor" id="user-content-objectgetownpropertydescriptors"><svg  class="octicon octicon-link" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Object.getOwnPropertyDescriptors()</h2>
    <p>前面说过，<code>Object.getOwnPropertyDescriptor</code>方法会返回某个对象属性的描述对象（descriptor）。ES2017 引入了<code>Object.getOwnPropertyDescriptors</code>方法，返回指定对象所有自身属性（非继承属性）的描述对象。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">const</span> <span class="pl-c1">obj</span> <span class="pl-k">=</span> {
  foo<span class="pl-k">:</span> <span class="pl-c1">123</span>,
  <span class="pl-k">get</span> <span class="pl-en">bar</span>() { <span class="pl-k">return</span> <span class="pl-s"><span class="pl-pds">'</span>abc<span class="pl-pds">'</span></span> }
};

<span class="pl-c1">Object</span>.<span class="pl-en">getOwnPropertyDescriptors</span>(obj)
<span class="pl-c"><span class="pl-c">//</span> { foo:</span>
<span class="pl-c"><span class="pl-c">//</span>    { value: 123,</span>
<span class="pl-c"><span class="pl-c">//</span>      writable: true,</span>
<span class="pl-c"><span class="pl-c">//</span>      enumerable: true,</span>
<span class="pl-c"><span class="pl-c">//</span>      configurable: true },</span>
<span class="pl-c"><span class="pl-c">//</span>   bar:</span>
<span class="pl-c"><span class="pl-c">//</span>    { get: [Function: bar],</span>
<span class="pl-c"><span class="pl-c">//</span>      set: undefined,</span>
<span class="pl-c"><span class="pl-c">//</span>      enumerable: true,</span>
<span class="pl-c"><span class="pl-c">//</span>      configurable: true } }</span></pre></div>
    <p>上面代码中，<code>Object.getOwnPropertyDescriptors</code>方法返回一个对象，所有原对象的属性名都是该对象的属性名，对应的属性值就是该属性的描述对象。</p>
    <p>该方法的实现非常容易。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">function</span> <span class="pl-en">getOwnPropertyDescriptors</span>(<span class="pl-smi">obj</span>) {
  <span class="pl-k">const</span> <span class="pl-c1">result</span> <span class="pl-k">=</span> {};
  <span class="pl-k">for</span> (<span class="pl-k">let</span> key <span class="pl-k">of</span> <span class="pl-c1">Reflect</span>.<span class="pl-en">ownKeys</span>(obj)) {
    result[key] <span class="pl-k">=</span> <span class="pl-c1">Object</span>.<span class="pl-en">getOwnPropertyDescriptor</span>(obj, key);
  }
  <span class="pl-k">return</span> result;
}</pre></div>
    <p>该方法的引入目的，主要是为了解决<code>Object.assign()</code>无法正确拷贝<code>get</code>属性和<code>set</code>属性的问题。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">const</span> <span class="pl-c1">source</span> <span class="pl-k">=</span> {
  <span class="pl-k">set</span> <span class="pl-en">foo</span>(<span class="pl-smi">value</span>) {
    <span class="pl-en">console</span>.<span class="pl-c1">log</span>(value);
  }
};

<span class="pl-k">const</span> <span class="pl-c1">target1</span> <span class="pl-k">=</span> {};
<span class="pl-c1">Object</span>.<span class="pl-en">assign</span>(target1, source);

<span class="pl-c1">Object</span>.<span class="pl-en">getOwnPropertyDescriptor</span>(target1, <span class="pl-s"><span class="pl-pds">'</span>foo<span class="pl-pds">'</span></span>)
<span class="pl-c"><span class="pl-c">//</span> { value: undefined,</span>
<span class="pl-c"><span class="pl-c">//</span>   writable: true,</span>
<span class="pl-c"><span class="pl-c">//</span>   enumerable: true,</span>
<span class="pl-c"><span class="pl-c">//</span>   configurable: true }</span></pre></div>
    <p>上面代码中，<code>source</code>对象的<code>foo</code>属性的值是一个赋值函数，<code>Object.assign</code>方法将这个属性拷贝给<code>target1</code>对象，结果该属性的值变成了<code>undefined</code>。这是因为<code>Object.assign</code>方法总是拷贝一个属性的值，而不会拷贝它背后的赋值方法或取值方法。</p>
    <p>这时，<code>Object.getOwnPropertyDescriptors</code>方法配合<code>Object.defineProperties</code>方法，就可以实现正确拷贝。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">const</span> <span class="pl-c1">source</span> <span class="pl-k">=</span> {
  <span class="pl-k">set</span> <span class="pl-en">foo</span>(<span class="pl-smi">value</span>) {
    <span class="pl-en">console</span>.<span class="pl-c1">log</span>(value);
  }
};

<span class="pl-k">const</span> <span class="pl-c1">target2</span> <span class="pl-k">=</span> {};
<span class="pl-c1">Object</span>.<span class="pl-en">defineProperties</span>(target2, <span class="pl-c1">Object</span>.<span class="pl-en">getOwnPropertyDescriptors</span>(source));
<span class="pl-c1">Object</span>.<span class="pl-en">getOwnPropertyDescriptor</span>(target2, <span class="pl-s"><span class="pl-pds">'</span>foo<span class="pl-pds">'</span></span>)
<span class="pl-c"><span class="pl-c">//</span> { get: undefined,</span>
<span class="pl-c"><span class="pl-c">//</span>   set: [Function: foo],</span>
<span class="pl-c"><span class="pl-c">//</span>   enumerable: true,</span>
<span class="pl-c"><span class="pl-c">//</span>   configurable: true }</span></pre></div>
    <p>上面代码中，两个对象合并的逻辑可以写成一个函数。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">const</span> <span class="pl-c1">shallowMerge</span> <span class="pl-k">=</span> (<span class="pl-smi">target</span>, <span class="pl-smi">source</span>) <span class="pl-k">=&gt;</span> <span class="pl-c1">Object</span>.<span class="pl-en">defineProperties</span>(
  target,
  <span class="pl-c1">Object</span>.<span class="pl-en">getOwnPropertyDescriptors</span>(source)
);</pre></div>
    <p><code>Object.getOwnPropertyDescriptors</code>方法的另一个用处，是配合<code>Object.create</code>方法，将对象属性克隆到一个新对象。这属于浅拷贝。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">const</span> <span class="pl-c1">clone</span> <span class="pl-k">=</span> <span class="pl-c1">Object</span>.<span class="pl-en">create</span>(<span class="pl-c1">Object</span>.<span class="pl-en">getPrototypeOf</span>(obj),
  <span class="pl-c1">Object</span>.<span class="pl-en">getOwnPropertyDescriptors</span>(obj));

<span class="pl-c"><span class="pl-c">//</span> 或者</span>

<span class="pl-k">const</span> <span class="pl-c1">shallowClone</span> <span class="pl-k">=</span> (<span class="pl-smi">obj</span>) <span class="pl-k">=&gt;</span> <span class="pl-c1">Object</span>.<span class="pl-en">create</span>(
  <span class="pl-c1">Object</span>.<span class="pl-en">getPrototypeOf</span>(obj),
  <span class="pl-c1">Object</span>.<span class="pl-en">getOwnPropertyDescriptors</span>(obj)
);</pre></div>
    <p>上面代码会克隆对象<code>obj</code>。</p>
    <p>另外，<code>Object.getOwnPropertyDescriptors</code>方法可以实现一个对象继承另一个对象。以前，继承另一个对象，常常写成下面这样。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">const</span> <span class="pl-c1">obj</span> <span class="pl-k">=</span> {
  __proto__<span class="pl-k">:</span> prot,
  foo<span class="pl-k">:</span> <span class="pl-c1">123</span>,
};</pre></div>
    <p>ES6 规定<code>__proto__</code>只有浏览器要部署，其他环境不用部署。如果去除<code>__proto__</code>，上面代码就要改成下面这样。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">const</span> <span class="pl-c1">obj</span> <span class="pl-k">=</span> <span class="pl-c1">Object</span>.<span class="pl-en">create</span>(prot);
<span class="pl-smi">obj</span>.<span class="pl-smi">foo</span> <span class="pl-k">=</span> <span class="pl-c1">123</span>;

<span class="pl-c"><span class="pl-c">//</span> 或者</span>

<span class="pl-k">const</span> <span class="pl-c1">obj</span> <span class="pl-k">=</span> <span class="pl-c1">Object</span>.<span class="pl-en">assign</span>(
  <span class="pl-c1">Object</span>.<span class="pl-en">create</span>(prot),
  {
    foo<span class="pl-k">:</span> <span class="pl-c1">123</span>,
  }
);</pre></div>
    <p>有了<code>Object.getOwnPropertyDescriptors</code>，我们就有了另一种写法。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">const</span> <span class="pl-c1">obj</span> <span class="pl-k">=</span> <span class="pl-c1">Object</span>.<span class="pl-en">create</span>(
  prot,
  <span class="pl-c1">Object</span>.<span class="pl-en">getOwnPropertyDescriptors</span>({
    foo<span class="pl-k">:</span> <span class="pl-c1">123</span>,
  })
);</pre></div>
    <p><code>Object.getOwnPropertyDescriptors</code>也可以用来实现 Mixin（混入）模式。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">let</span> <span class="pl-en">mix</span> <span class="pl-k">=</span> (<span class="pl-smi">object</span>) <span class="pl-k">=&gt;</span> ({
  <span class="pl-en">with</span><span class="pl-k">:</span> (<span class="pl-k">...</span><span class="pl-v">mixins</span>) <span class="pl-k">=&gt;</span> <span class="pl-smi">mixins</span>.<span class="pl-en">reduce</span>(
    (<span class="pl-smi">c</span>, <span class="pl-smi">mixin</span>) <span class="pl-k">=&gt;</span> <span class="pl-c1">Object</span>.<span class="pl-en">create</span>(
      c, <span class="pl-c1">Object</span>.<span class="pl-en">getOwnPropertyDescriptors</span>(mixin)
    ), object)
});

<span class="pl-c"><span class="pl-c">//</span> multiple mixins example</span>
<span class="pl-k">let</span> a <span class="pl-k">=</span> {a<span class="pl-k">:</span> <span class="pl-s"><span class="pl-pds">'</span>a<span class="pl-pds">'</span></span>};
<span class="pl-k">let</span> b <span class="pl-k">=</span> {b<span class="pl-k">:</span> <span class="pl-s"><span class="pl-pds">'</span>b<span class="pl-pds">'</span></span>};
<span class="pl-k">let</span> c <span class="pl-k">=</span> {c<span class="pl-k">:</span> <span class="pl-s"><span class="pl-pds">'</span>c<span class="pl-pds">'</span></span>};
<span class="pl-k">let</span> d <span class="pl-k">=</span> <span class="pl-en">mix</span>(c).<span class="pl-en">with</span>(a, b);

<span class="pl-smi">d</span>.<span class="pl-smi">c</span> <span class="pl-c"><span class="pl-c">//</span> "c"</span>
<span class="pl-smi">d</span>.<span class="pl-smi">b</span> <span class="pl-c"><span class="pl-c">//</span> "b"</span>
<span class="pl-smi">d</span>.<span class="pl-smi">a</span> <span class="pl-c"><span class="pl-c">//</span> "a"</span></pre></div>
    <p>上面代码返回一个新的对象<code>d</code>，代表了对象<code>a</code>和<code>b</code>被混入了对象<code>c</code>的操作。</p>
    <p>出于完整性的考虑，<code>Object.getOwnPropertyDescriptors</code>进入标准以后，以后还会新增<code>Reflect.getOwnPropertyDescriptors</code>方法。</p>
    <h2><a href="#__proto__属性objectsetprototypeofobjectgetprototypeof"  class="anchor" id="user-content-__proto__属性objectsetprototypeofobjectgetprototypeof"><svg  class="octicon octicon-link" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a><code>__proto__</code>属性，Object.setPrototypeOf()，Object.getPrototypeOf()</h2>
    <p>JavaScript 语言的对象继承是通过原型链实现的。ES6 提供了更多原型对象的操作方法。</p>
    <h3><a href="#__proto__属性"  class="anchor" id="user-content-__proto__属性"><svg  class="octicon octicon-link" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a><code>__proto__</code>属性</h3>
    <p><code>__proto__</code>属性（前后各两个下划线），用来读取或设置当前对象的<code>prototype</code>对象。目前，所有浏览器（包括 IE11）都部署了这个属性。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-c"><span class="pl-c">//</span> es6 的写法</span>
<span class="pl-k">const</span> <span class="pl-c1">obj</span> <span class="pl-k">=</span> {
  <span class="pl-en">method</span><span class="pl-k">:</span> <span class="pl-k">function</span>() { <span class="pl-k">...</span> }
};
<span class="pl-smi">obj</span>.<span class="pl-smi">__proto__</span> <span class="pl-k">=</span> someOtherObj;

<span class="pl-c"><span class="pl-c">//</span> es5 的写法</span>
<span class="pl-k">var</span> obj <span class="pl-k">=</span> <span class="pl-c1">Object</span>.<span class="pl-en">create</span>(someOtherObj);
<span class="pl-smi">obj</span>.<span class="pl-en">method</span> <span class="pl-k">=</span> <span class="pl-k">function</span>() { <span class="pl-k">...</span> };</pre></div>
    <p>该属性没有写入 ES6 的正文，而是写入了附录，原因是<code>__proto__</code>前后的双下划线，说明它本质上是一个内部属性，而不是一个正式的对外的 API，只是由于浏览器广泛支持，才被加入了 ES6。标准明确规定，只有浏览器必须部署这个属性，其他运行环境不一定需要部署，而且新的代码最好认为这个属性是不存在的。因此，无论从语义的角度，还是从兼容性的角度，都不要使用这个属性，而是使用下面的<code>Object.setPrototypeOf()</code>（写操作）、<code>Object.getPrototypeOf()</code>（读操作）、<code>Object.create()</code>（生成操作）代替。</p>
    <p>实现上，<code>__proto__</code>调用的是<code>Object.prototype.__proto__</code>，具体实现如下。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-c1">Object</span>.<span class="pl-en">defineProperty</span>(<span class="pl-c1">Object</span>.<span class="pl-c1">prototype</span>, <span class="pl-s"><span class="pl-pds">'</span>__proto__<span class="pl-pds">'</span></span>, {
  <span class="pl-en">get</span>() {
    <span class="pl-k">let</span> _thisObj <span class="pl-k">=</span> <span class="pl-c1">Object</span>(<span class="pl-c1">this</span>);
    <span class="pl-k">return</span> <span class="pl-c1">Object</span>.<span class="pl-en">getPrototypeOf</span>(_thisObj);
  },
  <span class="pl-en">set</span>(<span class="pl-smi">proto</span>) {
    <span class="pl-k">if</span> (<span class="pl-c1">this</span> <span class="pl-k">===</span> <span class="pl-c1">undefined</span> <span class="pl-k">||</span> <span class="pl-c1">this</span> <span class="pl-k">===</span> <span class="pl-c1">null</span>) {
      <span class="pl-k">throw</span> <span class="pl-k">new</span> <span class="pl-en">TypeError</span>();
    }
    <span class="pl-k">if</span> (<span class="pl-k">!</span><span class="pl-en">isObject</span>(<span class="pl-c1">this</span>)) {
      <span class="pl-k">return</span> <span class="pl-c1">undefined</span>;
    }
    <span class="pl-k">if</span> (<span class="pl-k">!</span><span class="pl-en">isObject</span>(proto)) {
      <span class="pl-k">return</span> <span class="pl-c1">undefined</span>;
    }
    <span class="pl-k">let</span> status <span class="pl-k">=</span> <span class="pl-c1">Reflect</span>.<span class="pl-en">setPrototypeOf</span>(<span class="pl-c1">this</span>, proto);
    <span class="pl-k">if</span> (<span class="pl-k">!</span>status) {
      <span class="pl-k">throw</span> <span class="pl-k">new</span> <span class="pl-en">TypeError</span>();
    }
  },
});

<span class="pl-k">function</span> <span class="pl-en">isObject</span>(<span class="pl-smi">value</span>) {
  <span class="pl-k">return</span> <span class="pl-c1">Object</span>(value) <span class="pl-k">===</span> value;
}</pre></div>
    <p>如果一个对象本身部署了<code>__proto__</code>属性，该属性的值就是对象的原型。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-c1">Object</span>.<span class="pl-en">getPrototypeOf</span>({ __proto__<span class="pl-k">:</span> <span class="pl-c1">null</span> })
<span class="pl-c"><span class="pl-c">//</span> null</span></pre></div>
    <h3><a href="#objectsetprototypeof"  class="anchor" id="user-content-objectsetprototypeof"><svg  class="octicon octicon-link" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Object.setPrototypeOf()</h3>
    <p><code>Object.setPrototypeOf</code>方法的作用与<code>__proto__</code>相同，用来设置一个对象的<code>prototype</code>对象，返回参数对象本身。它是 ES6 正式推荐的设置原型对象的方法。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-c"><span class="pl-c">//</span> 格式</span>
<span class="pl-c1">Object</span>.<span class="pl-en">setPrototypeOf</span>(object, prototype)

<span class="pl-c"><span class="pl-c">//</span> 用法</span>
<span class="pl-k">const</span> <span class="pl-c1">o</span> <span class="pl-k">=</span> <span class="pl-c1">Object</span>.<span class="pl-en">setPrototypeOf</span>({}, <span class="pl-c1">null</span>);</pre></div>
    <p>该方法等同于下面的函数。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">function</span> (<span class="pl-smi">obj</span>, <span class="pl-smi">proto</span>) {
  <span class="pl-smi">obj</span>.<span class="pl-smi">__proto__</span> <span class="pl-k">=</span> proto;
  <span class="pl-k">return</span> obj;
}</pre></div>
    <p>下面是一个例子。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">let</span> proto <span class="pl-k">=</span> {};
<span class="pl-k">let</span> obj <span class="pl-k">=</span> { x<span class="pl-k">:</span> <span class="pl-c1">10</span> };
<span class="pl-c1">Object</span>.<span class="pl-en">setPrototypeOf</span>(obj, proto);

<span class="pl-smi">proto</span>.<span class="pl-c1">y</span> <span class="pl-k">=</span> <span class="pl-c1">20</span>;
<span class="pl-smi">proto</span>.<span class="pl-c1">z</span> <span class="pl-k">=</span> <span class="pl-c1">40</span>;

<span class="pl-smi">obj</span>.<span class="pl-c1">x</span> <span class="pl-c"><span class="pl-c">//</span> 10</span>
<span class="pl-smi">obj</span>.<span class="pl-c1">y</span> <span class="pl-c"><span class="pl-c">//</span> 20</span>
<span class="pl-smi">obj</span>.<span class="pl-c1">z</span> <span class="pl-c"><span class="pl-c">//</span> 40</span></pre></div>
    <p>上面代码将<code>proto</code>对象设为<code>obj</code>对象的原型，所以从<code>obj</code>对象可以读取<code>proto</code>对象的属性。</p>
    <p>如果第一个参数不是对象，会自动转为对象。但是由于返回的还是第一个参数，所以这个操作不会产生任何效果。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-c1">Object</span>.<span class="pl-en">setPrototypeOf</span>(<span class="pl-c1">1</span>, {}) <span class="pl-k">===</span> <span class="pl-c1">1</span> <span class="pl-c"><span class="pl-c">//</span> true</span>
<span class="pl-c1">Object</span>.<span class="pl-en">setPrototypeOf</span>(<span class="pl-s"><span class="pl-pds">'</span>foo<span class="pl-pds">'</span></span>, {}) <span class="pl-k">===</span> <span class="pl-s"><span class="pl-pds">'</span>foo<span class="pl-pds">'</span></span> <span class="pl-c"><span class="pl-c">//</span> true</span>
<span class="pl-c1">Object</span>.<span class="pl-en">setPrototypeOf</span>(<span class="pl-c1">true</span>, {}) <span class="pl-k">===</span> <span class="pl-c1">true</span> <span class="pl-c"><span class="pl-c">//</span> true</span></pre></div>
    <p>由于<code>undefined</code>和<code>null</code>无法转为对象，所以如果第一个参数是<code>undefined</code>或<code>null</code>，就会报错。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-c1">Object</span>.<span class="pl-en">setPrototypeOf</span>(<span class="pl-c1">undefined</span>, {})
<span class="pl-c"><span class="pl-c">//</span> TypeError: Object.setPrototypeOf called on null or undefined</span>

<span class="pl-c1">Object</span>.<span class="pl-en">setPrototypeOf</span>(<span class="pl-c1">null</span>, {})
<span class="pl-c"><span class="pl-c">//</span> TypeError: Object.setPrototypeOf called on null or undefined</span></pre></div>
    <h3><a href="#objectgetprototypeof"  class="anchor" id="user-content-objectgetprototypeof"><svg  class="octicon octicon-link" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Object.getPrototypeOf()</h3>
    <p>该方法与<code>Object.setPrototypeOf</code>方法配套，用于读取一个对象的原型对象。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-c1">Object</span>.<span class="pl-en">getPrototypeOf</span>(obj);</pre></div>
    <p>下面是一个例子。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">function</span> <span class="pl-en">Rectangle</span>() {
  <span class="pl-c"><span class="pl-c">//</span> ...</span>
}

<span class="pl-k">const</span> <span class="pl-c1">rec</span> <span class="pl-k">=</span> <span class="pl-k">new</span> <span class="pl-en">Rectangle</span>();

<span class="pl-c1">Object</span>.<span class="pl-en">getPrototypeOf</span>(rec) <span class="pl-k">===</span> <span class="pl-smi">Rectangle</span>.<span class="pl-c1">prototype</span>
<span class="pl-c"><span class="pl-c">//</span> true</span>

<span class="pl-c1">Object</span>.<span class="pl-en">setPrototypeOf</span>(rec, <span class="pl-c1">Object</span>.<span class="pl-c1">prototype</span>);
<span class="pl-c1">Object</span>.<span class="pl-en">getPrototypeOf</span>(rec) <span class="pl-k">===</span> <span class="pl-smi">Rectangle</span>.<span class="pl-c1">prototype</span>
<span class="pl-c"><span class="pl-c">//</span> false</span></pre></div>
    <p>如果参数不是对象，会被自动转为对象。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-c"><span class="pl-c">//</span> 等同于 Object.getPrototypeOf(Number(1))</span>
<span class="pl-c1">Object</span>.<span class="pl-en">getPrototypeOf</span>(<span class="pl-c1">1</span>)
<span class="pl-c"><span class="pl-c">//</span> Number {[[PrimitiveValue]]: 0}</span>

<span class="pl-c"><span class="pl-c">//</span> 等同于 Object.getPrototypeOf(String('foo'))</span>
<span class="pl-c1">Object</span>.<span class="pl-en">getPrototypeOf</span>(<span class="pl-s"><span class="pl-pds">'</span>foo<span class="pl-pds">'</span></span>)
<span class="pl-c"><span class="pl-c">//</span> String {length: 0, [[PrimitiveValue]]: ""}</span>

<span class="pl-c"><span class="pl-c">//</span> 等同于 Object.getPrototypeOf(Boolean(true))</span>
<span class="pl-c1">Object</span>.<span class="pl-en">getPrototypeOf</span>(<span class="pl-c1">true</span>)
<span class="pl-c"><span class="pl-c">//</span> Boolean {[[PrimitiveValue]]: false}</span>

<span class="pl-c1">Object</span>.<span class="pl-en">getPrototypeOf</span>(<span class="pl-c1">1</span>) <span class="pl-k">===</span> <span class="pl-c1">Number</span>.<span class="pl-c1">prototype</span> <span class="pl-c"><span class="pl-c">//</span> true</span>
<span class="pl-c1">Object</span>.<span class="pl-en">getPrototypeOf</span>(<span class="pl-s"><span class="pl-pds">'</span>foo<span class="pl-pds">'</span></span>) <span class="pl-k">===</span> <span class="pl-c1">String</span>.<span class="pl-c1">prototype</span> <span class="pl-c"><span class="pl-c">//</span> true</span>
<span class="pl-c1">Object</span>.<span class="pl-en">getPrototypeOf</span>(<span class="pl-c1">true</span>) <span class="pl-k">===</span> <span class="pl-c1">Boolean</span>.<span class="pl-c1">prototype</span> <span class="pl-c"><span class="pl-c">//</span> true</span></pre></div>
    <p>如果参数是<code>undefined</code>或<code>null</code>，它们无法转为对象，所以会报错。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-c1">Object</span>.<span class="pl-en">getPrototypeOf</span>(<span class="pl-c1">null</span>)
<span class="pl-c"><span class="pl-c">//</span> TypeError: Cannot convert undefined or null to object</span>

<span class="pl-c1">Object</span>.<span class="pl-en">getPrototypeOf</span>(<span class="pl-c1">undefined</span>)
<span class="pl-c"><span class="pl-c">//</span> TypeError: Cannot convert undefined or null to object</span></pre></div>
    <h2><a href="#super-关键字"  class="anchor" id="user-content-super-关键字"><svg  class="octicon octicon-link" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>super 关键字</h2>
    <p>我们知道，<code>this</code>关键字总是指向函数所在的当前对象，ES6 又新增了另一个类似的关键字<code>super</code>，指向当前对象的原型对象。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">const</span> <span class="pl-c1">proto</span> <span class="pl-k">=</span> {
  foo<span class="pl-k">:</span> <span class="pl-s"><span class="pl-pds">'</span>hello<span class="pl-pds">'</span></span>
};

<span class="pl-k">const</span> <span class="pl-c1">obj</span> <span class="pl-k">=</span> {
  <span class="pl-en">find</span>() {
    <span class="pl-k">return</span> <span class="pl-c1">super</span>.<span class="pl-smi">foo</span>;
  }
};

<span class="pl-c1">Object</span>.<span class="pl-en">setPrototypeOf</span>(obj, proto);
<span class="pl-smi">obj</span>.<span class="pl-c1">find</span>() <span class="pl-c"><span class="pl-c">//</span> "hello"</span></pre></div>
    <p>上面代码中，对象<code>obj</code>的<code>find</code>方法之中，通过<code>super.foo</code>引用了原型对象<code>proto</code>的<code>foo</code>属性。</p>
    <p>注意，<code>super</code>关键字表示原型对象时，只能用在对象的方法之中，用在其他地方都会报错。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-c"><span class="pl-c">//</span> 报错</span>
<span class="pl-k">const</span> <span class="pl-c1">obj</span> <span class="pl-k">=</span> {
  foo<span class="pl-k">:</span> <span class="pl-c1">super</span>.<span class="pl-smi">foo</span>
}

<span class="pl-c"><span class="pl-c">//</span> 报错</span>
<span class="pl-k">const</span> <span class="pl-c1">obj</span> <span class="pl-k">=</span> {
  <span class="pl-en">foo</span><span class="pl-k">:</span> () <span class="pl-k">=&gt;</span> <span class="pl-c1">super</span>.<span class="pl-smi">foo</span>
}

<span class="pl-c"><span class="pl-c">//</span> 报错</span>
<span class="pl-k">const</span> <span class="pl-c1">obj</span> <span class="pl-k">=</span> {
  <span class="pl-en">foo</span><span class="pl-k">:</span> <span class="pl-k">function</span> () {
    <span class="pl-k">return</span> <span class="pl-c1">super</span>.<span class="pl-smi">foo</span>
  }
}</pre></div>
    <p>上面三种<code>super</code>的用法都会报错，因为对于 JavaScript 引擎来说，这里的<code>super</code>都没有用在对象的方法之中。第一种写法是<code>super</code>用在属性里面，第二种和第三种写法是<code>super</code>用在一个函数里面，然后赋值给<code>foo</code>属性。目前，只有对象方法的简写法可以让 JavaScript 引擎确认，定义的是对象的方法。</p>
    <p>JavaScript 引擎内部，<code>super.foo</code>等同于<code>Object.getPrototypeOf(this).foo</code>（属性）或<code>Object.getPrototypeOf(this).foo.call(this)</code>（方法）。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">const</span> <span class="pl-c1">proto</span> <span class="pl-k">=</span> {
  x<span class="pl-k">:</span> <span class="pl-s"><span class="pl-pds">'</span>hello<span class="pl-pds">'</span></span>,
  <span class="pl-en">foo</span>() {
    <span class="pl-en">console</span>.<span class="pl-c1">log</span>(<span class="pl-c1">this</span>.<span class="pl-c1">x</span>);
  },
};

<span class="pl-k">const</span> <span class="pl-c1">obj</span> <span class="pl-k">=</span> {
  x<span class="pl-k">:</span> <span class="pl-s"><span class="pl-pds">'</span>world<span class="pl-pds">'</span></span>,
  <span class="pl-en">foo</span>() {
    <span class="pl-c1">super</span>.<span class="pl-en">foo</span>();
  }
}

<span class="pl-c1">Object</span>.<span class="pl-en">setPrototypeOf</span>(obj, proto);

<span class="pl-smi">obj</span>.<span class="pl-en">foo</span>() <span class="pl-c"><span class="pl-c">//</span> "world"</span></pre></div>
    <p>上面代码中，<code>super.foo</code>指向原型对象<code>proto</code>的<code>foo</code>方法，但是绑定的<code>this</code>却还是当前对象<code>obj</code>，因此输出的就是<code>world</code>。</p>
    <h2><a href="#objectkeysobjectvaluesobjectentries"  class="anchor" id="user-content-objectkeysobjectvaluesobjectentries"><svg  class="octicon octicon-link" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Object.keys()，Object.values()，Object.entries()</h2>
    <h3><a href="#objectkeys"  class="anchor" id="user-content-objectkeys"><svg  class="octicon octicon-link" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Object.keys()</h3>
    <p>ES5 引入了<code>Object.keys</code>方法，返回一个数组，成员是参数对象自身的（不含继承的）所有可遍历（enumerable）属性的键名。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">var</span> obj <span class="pl-k">=</span> { foo<span class="pl-k">:</span> <span class="pl-s"><span class="pl-pds">'</span>bar<span class="pl-pds">'</span></span>, baz<span class="pl-k">:</span> <span class="pl-c1">42</span> };
<span class="pl-c1">Object</span>.<span class="pl-c1">keys</span>(obj)
<span class="pl-c"><span class="pl-c">//</span> ["foo", "baz"]</span></pre></div>
    <p>ES2017 <a href="https://github.com/tc39/proposal-object-values-entries">引入</a>了跟<code>Object.keys</code>配套的<code>Object.values</code>和<code>Object.entries</code>，作为遍历一个对象的补充手段，供<code>for...of</code>循环使用。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">let</span> {keys, values, entries} <span class="pl-k">=</span> <span class="pl-c1">Object</span>;
<span class="pl-k">let</span> obj <span class="pl-k">=</span> { a<span class="pl-k">:</span> <span class="pl-c1">1</span>, b<span class="pl-k">:</span> <span class="pl-c1">2</span>, c<span class="pl-k">:</span> <span class="pl-c1">3</span> };

<span class="pl-k">for</span> (<span class="pl-k">let</span> key <span class="pl-k">of</span> <span class="pl-en">keys</span>(obj)) {
  <span class="pl-en">console</span>.<span class="pl-c1">log</span>(key); <span class="pl-c"><span class="pl-c">//</span> 'a', 'b', 'c'</span>
}

<span class="pl-k">for</span> (<span class="pl-k">let</span> value <span class="pl-k">of</span> <span class="pl-en">values</span>(obj)) {
  <span class="pl-en">console</span>.<span class="pl-c1">log</span>(value); <span class="pl-c"><span class="pl-c">//</span> 1, 2, 3</span>
}

<span class="pl-k">for</span> (<span class="pl-k">let</span> [key, value] <span class="pl-k">of</span> <span class="pl-en">entries</span>(obj)) {
  <span class="pl-en">console</span>.<span class="pl-c1">log</span>([key, value]); <span class="pl-c"><span class="pl-c">//</span> ['a', 1], ['b', 2], ['c', 3]</span>
}</pre></div>
    <h3><a href="#objectvalues"  class="anchor" id="user-content-objectvalues"><svg  class="octicon octicon-link" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Object.values()</h3>
    <p><code>Object.values</code>方法返回一个数组，成员是参数对象自身的（不含继承的）所有可遍历（enumerable）属性的键值。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">const</span> <span class="pl-c1">obj</span> <span class="pl-k">=</span> { foo<span class="pl-k">:</span> <span class="pl-s"><span class="pl-pds">'</span>bar<span class="pl-pds">'</span></span>, baz<span class="pl-k">:</span> <span class="pl-c1">42</span> };
<span class="pl-c1">Object</span>.<span class="pl-c1">values</span>(obj)
<span class="pl-c"><span class="pl-c">//</span> ["bar", 42]</span></pre></div>
    <p>返回数组的成员顺序，与本章的《属性的遍历》部分介绍的排列规则一致。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">const</span> <span class="pl-c1">obj</span> <span class="pl-k">=</span> { <span class="pl-c1">100</span><span class="pl-k">:</span> <span class="pl-s"><span class="pl-pds">'</span>a<span class="pl-pds">'</span></span>, <span class="pl-c1">2</span><span class="pl-k">:</span> <span class="pl-s"><span class="pl-pds">'</span>b<span class="pl-pds">'</span></span>, <span class="pl-c1">7</span><span class="pl-k">:</span> <span class="pl-s"><span class="pl-pds">'</span>c<span class="pl-pds">'</span></span> };
<span class="pl-c1">Object</span>.<span class="pl-c1">values</span>(obj)
<span class="pl-c"><span class="pl-c">//</span> ["b", "c", "a"]</span></pre></div>
    <p>上面代码中，属性名为数值的属性，是按照数值大小，从小到大遍历的，因此返回的顺序是<code>b</code>、<code>c</code>、<code>a</code>。</p>
    <p><code>Object.values</code>只返回对象自身的可遍历属性。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">const</span> <span class="pl-c1">obj</span> <span class="pl-k">=</span> <span class="pl-c1">Object</span>.<span class="pl-en">create</span>({}, {p<span class="pl-k">:</span> {value<span class="pl-k">:</span> <span class="pl-c1">42</span>}});
<span class="pl-c1">Object</span>.<span class="pl-c1">values</span>(obj) <span class="pl-c"><span class="pl-c">//</span> []</span></pre></div>
    <p>上面代码中，<code>Object.create</code>方法的第二个参数添加的对象属性（属性<code>p</code>），如果不显式声明，默认是不可遍历的，因为<code>p</code>的属性描述对象的<code>enumerable</code>默认是<code>false</code>，<code>Object.values</code>不会返回这个属性。只要把<code>enumerable</code>改成<code>true</code>，<code>Object.values</code>就会返回属性<code>p</code>的值。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">const</span> <span class="pl-c1">obj</span> <span class="pl-k">=</span> <span class="pl-c1">Object</span>.<span class="pl-en">create</span>({}, {p<span class="pl-k">:</span>
  {
 &nbsp; &nbsp;value<span class="pl-k">:</span> <span class="pl-c1">42</span>,
 &nbsp; &nbsp;enumerable<span class="pl-k">:</span> <span class="pl-c1">true</span>
 &nbsp;}
});
<span class="pl-c1">Object</span>.<span class="pl-c1">values</span>(obj) <span class="pl-c"><span class="pl-c">//</span> [42]</span></pre></div>
    <p><code>Object.values</code>会过滤属性名为 Symbol 值的属性。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-c1">Object</span>.<span class="pl-c1">values</span>({ [<span class="pl-c1">Symbol</span>()]<span class="pl-k">:</span> <span class="pl-c1">123</span>, foo<span class="pl-k">:</span> <span class="pl-s"><span class="pl-pds">'</span>abc<span class="pl-pds">'</span></span> });
<span class="pl-c"><span class="pl-c">//</span> ['abc']</span></pre></div>
    <p>如果<code>Object.values</code>方法的参数是一个字符串，会返回各个字符组成的一个数组。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-c1">Object</span>.<span class="pl-c1">values</span>(<span class="pl-s"><span class="pl-pds">'</span>foo<span class="pl-pds">'</span></span>)
<span class="pl-c"><span class="pl-c">//</span> ['f', 'o', 'o']</span></pre></div>
    <p>上面代码中，字符串会先转成一个类似数组的对象。字符串的每个字符，就是该对象的一个属性。因此，<code>Object.values</code>返回每个属性的键值，就是各个字符组成的一个数组。</p>
    <p>如果参数不是对象，<code>Object.values</code>会先将其转为对象。由于数值和布尔值的包装对象，都不会为实例添加非继承的属性。所以，<code>Object.values</code>会返回空数组。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-c1">Object</span>.<span class="pl-c1">values</span>(<span class="pl-c1">42</span>) <span class="pl-c"><span class="pl-c">//</span> []</span>
<span class="pl-c1">Object</span>.<span class="pl-c1">values</span>(<span class="pl-c1">true</span>) <span class="pl-c"><span class="pl-c">//</span> []</span></pre></div>
    <h3><a href="#objectentries"  class="anchor" id="user-content-objectentries"><svg  class="octicon octicon-link" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Object.entries</h3>
    <p><code>Object.entries</code>方法返回一个数组，成员是参数对象自身的（不含继承的）所有可遍历（enumerable）属性的键值对数组。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">const</span> <span class="pl-c1">obj</span> <span class="pl-k">=</span> { foo<span class="pl-k">:</span> <span class="pl-s"><span class="pl-pds">'</span>bar<span class="pl-pds">'</span></span>, baz<span class="pl-k">:</span> <span class="pl-c1">42</span> };
<span class="pl-c1">Object</span>.<span class="pl-c1">entries</span>(obj)
<span class="pl-c"><span class="pl-c">//</span> [ ["foo", "bar"], ["baz", 42] ]</span></pre></div>
    <p>除了返回值不一样，该方法的行为与<code>Object.values</code>基本一致。</p>
    <p>如果原对象的属性名是一个 Symbol 值，该属性会被忽略。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-c1">Object</span>.<span class="pl-c1">entries</span>({ [<span class="pl-c1">Symbol</span>()]<span class="pl-k">:</span> <span class="pl-c1">123</span>, foo<span class="pl-k">:</span> <span class="pl-s"><span class="pl-pds">'</span>abc<span class="pl-pds">'</span></span> });
<span class="pl-c"><span class="pl-c">//</span> [ [ 'foo', 'abc' ] ]</span></pre></div>
    <p>上面代码中，原对象有两个属性，<code>Object.entries</code>只输出属性名非 Symbol 值的属性。将来可能会有<code>Reflect.ownEntries()</code>方法，返回对象自身的所有属性。</p>
    <p><code>Object.entries</code>的基本用途是遍历对象的属性。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">let</span> obj <span class="pl-k">=</span> { one<span class="pl-k">:</span> <span class="pl-c1">1</span>, two<span class="pl-k">:</span> <span class="pl-c1">2</span> };
<span class="pl-k">for</span> (<span class="pl-k">let</span> [k, v] <span class="pl-k">of</span> <span class="pl-c1">Object</span>.<span class="pl-c1">entries</span>(obj)) {
  <span class="pl-en">console</span>.<span class="pl-c1">log</span>(
    <span class="pl-s"><span class="pl-pds">`</span><span class="pl-s1"><span class="pl-pse">${</span><span class="pl-c1">JSON</span>.<span class="pl-c1">stringify</span>(k)<span class="pl-pse">}</span></span>: <span class="pl-s1"><span class="pl-pse">${</span><span class="pl-c1">JSON</span>.<span class="pl-c1">stringify</span>(v)<span class="pl-pse">}</span></span><span class="pl-pds">`</span></span>
  );
}
<span class="pl-c"><span class="pl-c">//</span> "one": 1</span>
<span class="pl-c"><span class="pl-c">//</span> "two": 2</span></pre></div>
    <p><code>Object.entries</code>方法的另一个用处是，将对象转为真正的<code>Map</code>结构。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">const</span> <span class="pl-c1">obj</span> <span class="pl-k">=</span> { foo<span class="pl-k">:</span> <span class="pl-s"><span class="pl-pds">'</span>bar<span class="pl-pds">'</span></span>, baz<span class="pl-k">:</span> <span class="pl-c1">42</span> };
<span class="pl-k">const</span> <span class="pl-c1">map</span> <span class="pl-k">=</span> <span class="pl-k">new</span> <span class="pl-en">Map</span>(<span class="pl-c1">Object</span>.<span class="pl-c1">entries</span>(obj));
map <span class="pl-c"><span class="pl-c">//</span> Map { foo: "bar", baz: 42 }</span></pre></div>
    <p>自己实现<code>Object.entries</code>方法，非常简单。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-c"><span class="pl-c">//</span> Generator函数的版本</span>
<span class="pl-k">function</span><span class="pl-k">*</span> <span class="pl-en">entries</span>(<span class="pl-smi">obj</span>) {
  <span class="pl-k">for</span> (<span class="pl-k">let</span> key <span class="pl-k">of</span> <span class="pl-c1">Object</span>.<span class="pl-c1">keys</span>(obj)) {
    <span class="pl-k">yield</span> [key, obj[key]];
  }
}

<span class="pl-c"><span class="pl-c">//</span> 非Generator函数的版本</span>
<span class="pl-k">function</span> <span class="pl-en">entries</span>(<span class="pl-smi">obj</span>) {
  <span class="pl-k">let</span> arr <span class="pl-k">=</span> [];
  <span class="pl-k">for</span> (<span class="pl-k">let</span> key <span class="pl-k">of</span> <span class="pl-c1">Object</span>.<span class="pl-c1">keys</span>(obj)) {
    <span class="pl-smi">arr</span>.<span class="pl-c1">push</span>([key, obj[key]]);
  }
  <span class="pl-k">return</span> arr;
}</pre></div>
    <h2><a href="#对象的扩展运算符"  class="anchor" id="user-content-对象的扩展运算符"><svg  class="octicon octicon-link" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>对象的扩展运算符</h2>
    <p>《数组的扩展》一章中，已经介绍过扩展运算符（<code>...</code>）。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">const</span> [<span class="pl-c1">a</span>, <span class="pl-k">...</span><span class="pl-c1">b</span>] <span class="pl-k">=</span> [<span class="pl-c1">1</span>, <span class="pl-c1">2</span>, <span class="pl-c1">3</span>];
a <span class="pl-c"><span class="pl-c">//</span> 1</span>
b <span class="pl-c"><span class="pl-c">//</span> [2, 3]</span></pre></div>
    <p>ES2017 将这个运算符<a href="https://github.com/sebmarkbage/ecmascript-rest-spread">引入</a>了对象。</p>
    <p><strong>（1）解构赋值</strong></p>
    <p>对象的解构赋值用于从一个对象取值，相当于将所有可遍历的、但尚未被读取的属性，分配到指定的对象上面。所有的键和它们的值，都会拷贝到新对象上面。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">let</span> { x, y, <span class="pl-k">...</span>z } <span class="pl-k">=</span> { x<span class="pl-k">:</span> <span class="pl-c1">1</span>, y<span class="pl-k">:</span> <span class="pl-c1">2</span>, a<span class="pl-k">:</span> <span class="pl-c1">3</span>, b<span class="pl-k">:</span> <span class="pl-c1">4</span> };
x <span class="pl-c"><span class="pl-c">//</span> 1</span>
y <span class="pl-c"><span class="pl-c">//</span> 2</span>
z <span class="pl-c"><span class="pl-c">//</span> { a: 3, b: 4 }</span></pre></div>
    <p>上面代码中，变量<code>z</code>是解构赋值所在的对象。它获取等号右边的所有尚未读取的键（<code>a</code>和<code>b</code>），将它们连同值一起拷贝过来。</p>
    <p>由于解构赋值要求等号右边是一个对象，所以如果等号右边是<code>undefined</code>或<code>null</code>，就会报错，因为它们无法转为对象。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">let</span> { x, y, <span class="pl-k">...</span>z } <span class="pl-k">=</span> <span class="pl-c1">null</span>; <span class="pl-c"><span class="pl-c">//</span> 运行时错误</span>
<span class="pl-k">let</span> { x, y, <span class="pl-k">...</span>z } <span class="pl-k">=</span> <span class="pl-c1">undefined</span>; <span class="pl-c"><span class="pl-c">//</span> 运行时错误</span></pre></div>
    <p>解构赋值必须是最后一个参数，否则会报错。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">let</span> { <span class="pl-k">...</span>x, y, z } <span class="pl-k">=</span> obj; <span class="pl-c"><span class="pl-c">//</span> 句法错误</span>
<span class="pl-k">let</span> { x, <span class="pl-k">...</span>y, <span class="pl-k">...</span>z } <span class="pl-k">=</span> obj; <span class="pl-c"><span class="pl-c">//</span> 句法错误</span></pre></div>
    <p>上面代码中，解构赋值不是最后一个参数，所以会报错。</p>
    <p>注意，解构赋值的拷贝是浅拷贝，即如果一个键的值是复合类型的值（数组、对象、函数）、那么解构赋值拷贝的是这个值的引用，而不是这个值的副本。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">let</span> obj <span class="pl-k">=</span> { a<span class="pl-k">:</span> { b<span class="pl-k">:</span> <span class="pl-c1">1</span> } };
<span class="pl-k">let</span> { <span class="pl-k">...</span>x } <span class="pl-k">=</span> obj;
<span class="pl-smi">obj</span>.<span class="pl-smi">a</span>.<span class="pl-smi">b</span> <span class="pl-k">=</span> <span class="pl-c1">2</span>;
<span class="pl-smi">x</span>.<span class="pl-smi">a</span>.<span class="pl-smi">b</span> <span class="pl-c"><span class="pl-c">//</span> 2</span></pre></div>
    <p>上面代码中，<code>x</code>是解构赋值所在的对象，拷贝了对象<code>obj</code>的<code>a</code>属性。<code>a</code>属性引用了一个对象，修改这个对象的值，会影响到解构赋值对它的引用。</p>
    <p>另外，扩展运算符的解构赋值，不能复制继承自原型对象的属性。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">let</span> o1 <span class="pl-k">=</span> { a<span class="pl-k">:</span> <span class="pl-c1">1</span> };
<span class="pl-k">let</span> o2 <span class="pl-k">=</span> { b<span class="pl-k">:</span> <span class="pl-c1">2</span> };
<span class="pl-smi">o2</span>.<span class="pl-smi">__proto__</span> <span class="pl-k">=</span> o1;
<span class="pl-k">let</span> { <span class="pl-k">...</span>o3 } <span class="pl-k">=</span> o2;
o3 <span class="pl-c"><span class="pl-c">//</span> { b: 2 }</span>
<span class="pl-smi">o3</span>.<span class="pl-smi">a</span> <span class="pl-c"><span class="pl-c">//</span> undefined</span></pre></div>
    <p>上面代码中，对象<code>o3</code>复制了<code>o2</code>，但是只复制了<code>o2</code>自身的属性，没有复制它的原型对象<code>o1</code>的属性。</p>
    <p>下面是另一个例子。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">const</span> <span class="pl-c1">o</span> <span class="pl-k">=</span> <span class="pl-c1">Object</span>.<span class="pl-en">create</span>({ x<span class="pl-k">:</span> <span class="pl-c1">1</span>, y<span class="pl-k">:</span> <span class="pl-c1">2</span> });
<span class="pl-smi">o</span>.<span class="pl-c1">z</span> <span class="pl-k">=</span> <span class="pl-c1">3</span>;

<span class="pl-k">let</span> { x, <span class="pl-k">...</span>{ y, z } } <span class="pl-k">=</span> o;
x <span class="pl-c"><span class="pl-c">//</span> 1</span>
y <span class="pl-c"><span class="pl-c">//</span> undefined</span>
z <span class="pl-c"><span class="pl-c">//</span> 3</span></pre></div>
    <p>上面代码中，变量<code>x</code>是单纯的解构赋值，所以可以读取对象<code>o</code>继承的属性；变量<code>y</code>和<code>z</code>是扩展运算符的解构赋值，只能读取对象<code>o</code>自身的属性，所以变量<code>z</code>可以赋值成功，变量<code>y</code>取不到值。</p>
    <p>解构赋值的一个用处，是扩展某个函数的参数，引入其他操作。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">function</span> <span class="pl-en">baseFunction</span>({ a, b }) {
  <span class="pl-c"><span class="pl-c">//</span> ...</span>
}
<span class="pl-k">function</span> <span class="pl-en">wrapperFunction</span>({ x, y, <span class="pl-k">...</span>restConfig }) {
  <span class="pl-c"><span class="pl-c">//</span> 使用x和y参数进行操作</span>
  <span class="pl-c"><span class="pl-c">//</span> 其余参数传给原始函数</span>
  <span class="pl-k">return</span> <span class="pl-en">baseFunction</span>(restConfig);
}</pre></div>
    <p>上面代码中，原始函数<code>baseFunction</code>接受<code>a</code>和<code>b</code>作为参数，函数<code>wrapperFunction</code>在<code>baseFunction</code>的基础上进行了扩展，能够接受多余的参数，并且保留原始函数的行为。</p>
    <p><strong>（2）扩展运算符</strong></p>
    <p>扩展运算符（<code>...</code>）用于取出参数对象的所有可遍历属性，拷贝到当前对象之中。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">let</span> z <span class="pl-k">=</span> { a<span class="pl-k">:</span> <span class="pl-c1">3</span>, b<span class="pl-k">:</span> <span class="pl-c1">4</span> };
<span class="pl-k">let</span> n <span class="pl-k">=</span> { <span class="pl-k">...</span>z };
n <span class="pl-c"><span class="pl-c">//</span> { a: 3, b: 4 }</span></pre></div>
    <p>这等同于使用<code>Object.assign</code>方法。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">let</span> aClone <span class="pl-k">=</span> { <span class="pl-k">...</span>a };
<span class="pl-c"><span class="pl-c">//</span> 等同于</span>
<span class="pl-k">let</span> aClone <span class="pl-k">=</span> <span class="pl-c1">Object</span>.<span class="pl-en">assign</span>({}, a);</pre></div>
    <p>上面的例子只是拷贝了对象实例的属性，如果想完整克隆一个对象，还拷贝对象原型的属性，可以采用下面的写法。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-c"><span class="pl-c">//</span> 写法一</span>
<span class="pl-k">const</span> <span class="pl-c1">clone1</span> <span class="pl-k">=</span> {
  __proto__<span class="pl-k">:</span> <span class="pl-c1">Object</span>.<span class="pl-en">getPrototypeOf</span>(obj),
  <span class="pl-k">...</span>obj
};

<span class="pl-c"><span class="pl-c">//</span> 写法二</span>
<span class="pl-k">const</span> <span class="pl-c1">clone2</span> <span class="pl-k">=</span> <span class="pl-c1">Object</span>.<span class="pl-en">assign</span>(
  <span class="pl-c1">Object</span>.<span class="pl-en">create</span>(<span class="pl-c1">Object</span>.<span class="pl-en">getPrototypeOf</span>(obj)),
  obj
);

<span class="pl-c"><span class="pl-c">//</span> 写法三</span>
<span class="pl-k">const</span> <span class="pl-c1">clone3</span> <span class="pl-k">=</span> <span class="pl-c1">Object</span>.<span class="pl-en">create</span>(
  <span class="pl-c1">Object</span>.<span class="pl-en">getPrototypeOf</span>(obj),
  <span class="pl-c1">Object</span>.<span class="pl-en">getOwnPropertyDescriptors</span>(obj)
)</pre></div>
    <p>上面代码中，写法一的<code>__proto__</code>属性在非浏览器的环境不一定部署，因此推荐使用写法二和写法三。</p>
    <p>扩展运算符可以用于合并两个对象。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">let</span> ab <span class="pl-k">=</span> { <span class="pl-k">...</span>a, <span class="pl-k">...</span>b };
<span class="pl-c"><span class="pl-c">//</span> 等同于</span>
<span class="pl-k">let</span> ab <span class="pl-k">=</span> <span class="pl-c1">Object</span>.<span class="pl-en">assign</span>({}, a, b);</pre></div>
    <p>如果用户自定义的属性，放在扩展运算符后面，则扩展运算符内部的同名属性会被覆盖掉。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">let</span> aWithOverrides <span class="pl-k">=</span> { <span class="pl-k">...</span>a, x<span class="pl-k">:</span> <span class="pl-c1">1</span>, y<span class="pl-k">:</span> <span class="pl-c1">2</span> };
<span class="pl-c"><span class="pl-c">//</span> 等同于</span>
<span class="pl-k">let</span> aWithOverrides <span class="pl-k">=</span> { <span class="pl-k">...</span>a, <span class="pl-k">...</span>{ x<span class="pl-k">:</span> <span class="pl-c1">1</span>, y<span class="pl-k">:</span> <span class="pl-c1">2</span> } };
<span class="pl-c"><span class="pl-c">//</span> 等同于</span>
<span class="pl-k">let</span> x <span class="pl-k">=</span> <span class="pl-c1">1</span>, y <span class="pl-k">=</span> <span class="pl-c1">2</span>, aWithOverrides <span class="pl-k">=</span> { <span class="pl-k">...</span>a, x, y };
<span class="pl-c"><span class="pl-c">//</span> 等同于</span>
<span class="pl-k">let</span> aWithOverrides <span class="pl-k">=</span> <span class="pl-c1">Object</span>.<span class="pl-en">assign</span>({}, a, { x<span class="pl-k">:</span> <span class="pl-c1">1</span>, y<span class="pl-k">:</span> <span class="pl-c1">2</span> });</pre></div>
    <p>上面代码中，<code>a</code>对象的<code>x</code>属性和<code>y</code>属性，拷贝到新对象后会被覆盖掉。</p>
    <p>这用来修改现有对象部分的属性就很方便了。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">let</span> newVersion <span class="pl-k">=</span> {
  <span class="pl-k">...</span>previousVersion,
  name<span class="pl-k">:</span> <span class="pl-s"><span class="pl-pds">'</span>New Name<span class="pl-pds">'</span></span> <span class="pl-c"><span class="pl-c">//</span> Override the name property</span>
};</pre></div>
    <p>上面代码中，<code>newVersion</code>对象自定义了<code>name</code>属性，其他属性全部复制自<code>previousVersion</code>对象。</p>
    <p>如果把自定义属性放在扩展运算符前面，就变成了设置新对象的默认属性值。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">let</span> aWithDefaults <span class="pl-k">=</span> { x<span class="pl-k">:</span> <span class="pl-c1">1</span>, y<span class="pl-k">:</span> <span class="pl-c1">2</span>, <span class="pl-k">...</span>a };
<span class="pl-c"><span class="pl-c">//</span> 等同于</span>
<span class="pl-k">let</span> aWithDefaults <span class="pl-k">=</span> <span class="pl-c1">Object</span>.<span class="pl-en">assign</span>({}, { x<span class="pl-k">:</span> <span class="pl-c1">1</span>, y<span class="pl-k">:</span> <span class="pl-c1">2</span> }, a);
<span class="pl-c"><span class="pl-c">//</span> 等同于</span>
<span class="pl-k">let</span> aWithDefaults <span class="pl-k">=</span> <span class="pl-c1">Object</span>.<span class="pl-en">assign</span>({ x<span class="pl-k">:</span> <span class="pl-c1">1</span>, y<span class="pl-k">:</span> <span class="pl-c1">2</span> }, a);</pre></div>
    <p>与数组的扩展运算符一样，对象的扩展运算符后面可以跟表达式。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">const</span> <span class="pl-c1">obj</span> <span class="pl-k">=</span> {
  <span class="pl-k">...</span>(x <span class="pl-k">&gt;</span> <span class="pl-c1">1</span> <span class="pl-k">?</span> {a<span class="pl-k">:</span> <span class="pl-c1">1</span>} <span class="pl-k">:</span> {}),
  b<span class="pl-k">:</span> <span class="pl-c1">2</span>,
};</pre></div>
    <p>如果扩展运算符后面是一个空对象，则没有任何效果。</p>
    <div class="highlight highlight-source-js"><pre>{<span class="pl-k">...</span>{}, a<span class="pl-k">:</span> <span class="pl-c1">1</span>}
<span class="pl-c"><span class="pl-c">//</span> { a: 1 }</span></pre></div>
    <p>如果扩展运算符的参数是<code>null</code>或<code>undefined</code>，这两个值会被忽略，不会报错。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">let</span> emptyObject <span class="pl-k">=</span> { <span class="pl-k">...</span>null, <span class="pl-k">...</span><span class="pl-c1">undefined</span> }; <span class="pl-c"><span class="pl-c">//</span> 不报错</span></pre></div>
    <p>扩展运算符的参数对象之中，如果有取值函数<code>get</code>，这个函数是会执行的。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-c"><span class="pl-c">//</span> 并不会抛出错误，因为 x 属性只是被定义，但没执行</span>
<span class="pl-k">let</span> aWithXGetter <span class="pl-k">=</span> {
  <span class="pl-k">...</span>a,
  <span class="pl-k">get</span> <span class="pl-en">x</span>() {
    <span class="pl-k">throw</span> <span class="pl-k">new</span> <span class="pl-en">Error</span>(<span class="pl-s"><span class="pl-pds">'</span>not throw yet<span class="pl-pds">'</span></span>);
  }
};

<span class="pl-c"><span class="pl-c">//</span> 会抛出错误，因为 x 属性被执行了</span>
<span class="pl-k">let</span> runtimeError <span class="pl-k">=</span> {
  <span class="pl-k">...</span>a,
  <span class="pl-k">...</span>{
    <span class="pl-k">get</span> <span class="pl-en">x</span>() {
      <span class="pl-k">throw</span> <span class="pl-k">new</span> <span class="pl-en">Error</span>(<span class="pl-s"><span class="pl-pds">'</span>throw now<span class="pl-pds">'</span></span>);
    }
  }
};</pre></div>
    <h2><a href="#null-传导运算符"  class="anchor" id="user-content-null-传导运算符"><svg  class="octicon octicon-link" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Null 传导运算符</h2>
    <p>编程实务中，如果读取对象内部的某个属性，往往需要判断一下该对象是否存在。比如，要读取<code>message.body.user.firstName</code>，安全的写法是写成下面这样。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">const</span> <span class="pl-c1">firstName</span> <span class="pl-k">=</span> (message
  <span class="pl-k">&amp;&amp;</span> <span class="pl-smi">message</span>.<span class="pl-c1">body</span>
  <span class="pl-k">&amp;&amp;</span> <span class="pl-smi">message</span>.<span class="pl-c1">body</span>.<span class="pl-smi">user</span>
  <span class="pl-k">&amp;&amp;</span> <span class="pl-smi">message</span>.<span class="pl-c1">body</span>.<span class="pl-smi">user</span>.<span class="pl-smi">firstName</span>) <span class="pl-k">||</span> <span class="pl-s"><span class="pl-pds">'</span>default<span class="pl-pds">'</span></span>;</pre></div>
    <p>这样的层层判断非常麻烦，因此现在有一个<a href="https://github.com/claudepache/es-optional-chaining">提案</a>，引入了“Null 传导运算符”（null propagation operator）<code>?.</code>，简化上面的写法。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">const</span> <span class="pl-c1">firstName</span> <span class="pl-k">=</span> message<span class="pl-k">?</span>.<span class="pl-c1">body</span><span class="pl-k">?</span>.<span class="pl-smi">user</span><span class="pl-k">?</span>.<span class="pl-smi">firstName</span> <span class="pl-k">||</span> <span class="pl-s"><span class="pl-pds">'</span>default<span class="pl-pds">'</span></span>;</pre></div>
    <p>上面代码有三个<code>?.</code>运算符，只要其中一个返回<code>null</code>或<code>undefined</code>，就不再往下运算，而是返回<code>undefined</code>。</p>
    <p>“Null 传导运算符”有四种用法。</p>
    <ul>
        <li><code>obj?.prop</code> // 读取对象属性</li>
        <li><code>obj?.[expr]</code> // 同上</li>
        <li><code>func?.(...args)</code> // 函数或对象方法的调用</li>
        <li><code>new C?.(...args)</code> // 构造函数的调用</li>
    </ul>
    <p>传导运算符之所以写成<code>obj?.prop</code>，而不是<code>obj?prop</code>，是为了方便编译器能够区分三元运算符<code>?:</code>（比如<code>obj?prop:123</code>）。</p>
    <p>下面是更多的例子。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-c"><span class="pl-c">//</span> 如果 a 是 null 或 undefined, 返回 undefined</span>
<span class="pl-c"><span class="pl-c">//</span> 否则返回 a.b.c().d</span>
a<span class="pl-k">?</span>.<span class="pl-smi">b</span>.<span class="pl-en">c</span>().<span class="pl-smi">d</span>

<span class="pl-c"><span class="pl-c">//</span> 如果 a 是 null 或 undefined，下面的语句不产生任何效果</span>
<span class="pl-c"><span class="pl-c">//</span> 否则执行 a.b = 42</span>
a<span class="pl-k">?</span>.<span class="pl-smi">b</span> <span class="pl-k">=</span> <span class="pl-c1">42</span>

<span class="pl-c"><span class="pl-c">//</span> 如果 a 是 null 或 undefined，下面的语句不产生任何效果</span>
<span class="pl-k">delete</span> a<span class="pl-k">?</span>.<span class="pl-smi">b</span></pre></div>
</div>
</body>
</html>
